MySQL 嵌套集合模型:利用 lft/rght 字段高效查询树形结构
在处理系统权限、分类目录等具有父子层级的数据时,传统的主外键自关联查询递归性能较差。本文介绍 MySQL 中的嵌套集合模型(Nested Set Model),通过为每个节点记录“左值”(lft)和“右值”(rght),将复杂的树形遍历转化为简单的区间判断查询。内容包括核心原理图解、查子/查父 SQL 写法、深度计算以及动态插入节点的操作流程。
嵌套集合模型原理
嵌套集合模型的核心思想是对树进行“后续遍历”,并为每个节点分配两个整数值:表示进入该节点子树的左边界 lft 和离开该节点子树的右边界 rght。
通过观察树结构的编号分布,可以发现:如果一个节点 A 的左右值范围完全包含了节点 B 的左右值范围(即 A.lft < B.lft AND A.rght > B.rght),那么 B 就是 A 的后代。这种基于数值的区间包含关系,使得我们无需执行递归即可进行高效的层级查询。

建表与初始化准备
为了演示操作,首先建立一张包含层级关系的评论表 comment,并在左值和右值上建立索引以提升查询速度。
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 ); 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$comment$left_num$right_num ON `comment` (`left_num`, `right_num`);
第 1 步:查询所有后代子节点
目的:获取指定节点(如节点 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;
第 2 步:查询所有祖先父节点
目的:获取指定节点(如节点 6)的所有上级节点,用于展示面包屑导航。
逻辑:找出那些其左右值范围“包住”了当前查询节点 c 的其他节点 p。
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;
第 3 步:计算指定节点的深度
注意:在 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'; -- 计算节点 4 的深度,根节点深度为 1 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;
第 4 步:获取子节点及其对应深度
目的:不仅获取子节点列表,还能显示它们在层级树中的相对深度。通常用于渲染缩进的菜单。
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;
第 5 步:向树结构中插入新节点
插入新节点是嵌套集合中最复杂且容易出错的操作。如果要在某节点(如节点 4)下添加一个新子节点(如节点 44,位置在其现有子节点左侧),需要遵循以下真实操作顺序,切勿直接 INSERT,否则会导致结构错乱。
5.1 准备阶段:更新现有点的值
由于要插入新节点占用空间,必须将目标节点右侧的所有现存节点的左右值全部加 2,腾出空位。
图中紫色部分表示需要变更的节点,绿色为新增节点。
更新思路:
1、将 left_num > 4.lft 的所有节点 left_num 加 2。
2、将 right_num > 4.lft 的所有节点 right_num 加 2。
-- 确认插入位置的基准点(此处假设插入到 left_num=6 之后) UPDATE comment SET left_num = left_num + 2 WHERE left_num > 6; UPDATE comment SET right_num = right_num + 2 WHERE right_num > 6;
5.2 执行阶段:插入新节点记录
为新节点赋予连续的左右值,使其刚好位于腾出的空隙中。
INSERT INTO comment SELECT 44, left_num + 1, left_num + 2 FROM comment WHERE comment_id = 4;
5.3 验证阶段
插入完成后,重新执行子节点查询,可以观察到节点 44 已正确归属于节点 4 下,且其他兄弟节点位置自动后移。

总结与适用场景
嵌套集合模型极其适合读取频繁、写入较少的场景,例如系统的地区分类、组织架构或永久性的栏目表。如果应用中存在大量的实时层级变更(如频繁的拖拽排序),嵌套集合的更新成本过高。在此类情况下,建议结合物化路径(Materialized Path)或使用消息队列(如 Kafka)异步处理层级同步,以平衡性能。
以上就是 MySQL 嵌套集合模型实现高效树形结构查询与操作的详细内容,更多关于数据库设计优化的资料请关注本站其它相关文章!免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









