早期使用 CakePHP ACL 插件時遇到 acos 表含 lft/rght 字段,不知其意直接修改導致數據錯亂。後來才發現這正是**嵌套集模型**的左右值編碼。本文系統梳理原理、查詢、插入、維護全流程。
對樹進行後序遍歷(左子樹 → 右子樹 → 根),每次進入節點遞增計數器賦予 left_num,離開節點再次遞增賦予 right_num。任意節點的子孫滿足:child.left BETWEEN parent.left AND parent.right。

DROP TABLE IF EXISTS `comment`; CREATE TABLE `comment` ( `comment_id` INT(11) DEFAULT NULL, `left_num` INT(11) DEFAULT NULL, `right_num` INT(11) DEFAULT NULL ) ENGINE=InnoDB; INSERT INTO `comment` VALUES (1,1,14), (2,2,5), (3,3,4), (4,6,13), (5,7,8), (6,9,12), (7,10,11); CREATE INDEX idx_lr ON `comment` (`left_num`, `right_num`);
SELECT c.* FROM comment AS p, comment AS c WHERE c.left_num BETWEEN p.left_num AND p.right_num AND p.comment_id = 4;
SELECT p.* FROM comment AS p, comment AS c WHERE c.left_num BETWEEN p.left_num AND p.right_num AND c.comment_id = 6;
-- MySQL 5.7+ 需調整 sql_mode 允許非聚合列 SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; SELECT c.*, COUNT(c.comment_id) AS depth FROM comment AS p, comment AS c WHERE c.left_num BETWEEN p.left_num AND p.right_num AND c.comment_id = 4 GROUP BY c.comment_id;
SELECT sub_child.*, (COUNT(sub_parent.comment_id) - 1) AS depth
FROM (
SELECT child.* FROM comment AS parent, comment AS child
WHERE child.left_num BETWEEN parent.left_num AND parent.right_num
AND parent.comment_id = 4
) AS sub_child,
(
SELECT child.* FROM comment AS parent, comment AS child
WHERE child.left_num BETWEEN parent.left_num AND parent.right_num
AND parent.comment_id = 4
) AS sub_parent
WHERE sub_child.left_num BETWEEN sub_parent.left_num AND sub_parent.right_num
GROUP BY sub_child.comment_id
ORDER BY sub_child.left_num;


-- 1. 獲取父節點 4 的左值 SELECT left_num FROM comment WHERE comment_id = 4; -- 6 -- 2. 所有左值 > 6 的 +2,右值 > 6 的 +2 UPDATE comment SET left_num = left_num + 2 WHERE left_num > 6; UPDATE comment SET right_num = right_num + 2 WHERE right_num > 6; -- 3. 插入新節點:左值=父左值+1=7,右值=父左值+2=8 INSERT INTO comment SELECT 44, left_num + 1, left_num + 2 FROM comment WHERE comment_id = 4;
-- 子孫查詢 SELECT c.* FROM comment AS p, comment AS c WHERE c.left_num BETWEEN p.left_num AND p.right_num AND p.comment_id = 4; -- 祖先查詢 SELECT p.* FROM comment AS p, comment AS c WHERE c.left_num BETWEEN p.left_num AND p.right_num AND c.comment_id = 44;
| 場景 | 推薦模型 | 理由 |
|---|---|---|
| 行政區劃、商品分類、權限樹 | 嵌套集 | 讀多寫少、層級深、頻繁查詢整棵子樹 |
| 評論回覆、無限級評論 | 鄰接表 + 遞歸 CTE (MySQL 8.0+) | 寫入頻繁、層級通常 < 5、需事務一致性 |
| 文件系統路徑、麵包屑導航 | 路徑枚舉 | 需全路徑字符串、LIKE 前綴匹配極快 |
SELECT COUNT(*) FROM t WHERE left_num >= right_num 檢測異常。id, pid,樹屬性表存 id, left_num, right_num, depth,降低主表寫入鎖競爭。嵌套集模型以空間換時間,將樹查詢轉化為索引範圍掃描,**查詢性能極強、寫入維護成本可控**。掌握「左值右值區間包含」這一核心判斷邏輯,即可靈活應對各類多層級業務查詢需求。