Mysql 递归查子孙 sql

查询下级单位ID(不包括自身)的SQL语句,
该SQL查询用于从daou表中查找指定单位id的下级单位ID,不包括自身。它使用了子查询和GROUP_CONCAT函数结合FIND_IN_SET来定位子单位,并通过level_跟踪层级。

daou 为单位表,parentid为表字段, #{value} 为想要查的单位id。

查下级不包括自身:

<select id="queryChildList" resultType="java.lang.String">
		SELECT T3.id
		FROM(
				SELECT
				     @codes as _ids,
					( SELECT @codes := GROUP_CONCAT(id)
						FROM daou
					   WHERE FIND_IN_SET(parentid, @codes)
					) as T1,
		  			@l := @l+1 as level_
				FROM daou, (SELECT @codes := #{value}, @l := 0 ) T4
				WHERE @codes IS NOT NULL
			) T2, daou T3
		WHERE FIND_IN_SET(T3.parentid, T2._ids)
</select>
<think> 我们参考了用户提供的引用内容,特别是引用[1]和引用[2]都提到了MySQL 8的递归查询使用`WITH RECURSIVE`语法。用户明确要求MySQL 8中进行递归查询的方法,因此我们将重点介绍`WITH RECURSIVE`的用法。 递归查询通常用于处理树形结构数据,比如菜单、地区、组织架构等。在MySQL 8之前,递归查询需要通过存储过程或函数来实现,而MySQL 8引入了通用表表达式(CTE)的递归功能,使得递归查询更加简便。 递归查询的基本结构包括两个部分: 1. 递归基(初始查询):这是递归的起点,选择初始记录。 2. 递归步(递归部分):通过连接递归基(或前一步递归的结果)和表本身,不断迭代,直到满足终止条件。 语法格式: ```sql WITH RECURSIVE 临时表名 AS ( -- 递归基(初始查询) SELECT ... FROM 表名 WHERE 初始条件 UNION ALL -- 递归步(递归部分) SELECT ... FROM 表名 JOIN 临时表名 ON 连接条件 WHERE 终止条件(可选) ) SELECT * FROM 临时表名; ``` 注意:递归查询必须使用UNION ALL(允许重复)或者UNION(去重),并且递归步中不能使用聚合函数、窗口函数、GROUP BY等。 下面我们通过两个例子来说明: 1. 自底向上(查询某个节点的所有父节点):例如查询id=12的所有父节点。 2. 自顶向下(查询某个节点的所有子节点):例如查询id=1的所有子节点。 假设我们有一个表`category`,包含字段`id`, `parent_id`, `name`。 例子1:查询id为12的所有父节点(包括自身?根据需求,通常不包括自身,但也可以包括,这里我们根据引用[1]的示例,它包括了自身,因为初始查询包含了id=12的记录,然后向上递归) 引用[1]中的例子是查询id=12的所有父节点,它包括了自身(因为从id=12开始),如果我们不希望包括自身,可以在最后的结果中过滤掉,或者在递归基中不选择它(但这样递归就会从它的父节点开始,所以通常包括自身再过滤)。 以下是引用[1]中的示例(包括自身): ```sql WITH RECURSIVE r AS ( -- 递归基:从id为12的记录开始 SELECT id, parent_id, name FROM category WHERE id = 12 UNION ALL -- 递归步:通过连接当前递归结果(r)和原表(c),找到r中记录的父节点 SELECT c.id, c.parent_id, c.name FROM category c INNER JOIN r ON r.parent_id = c.id ) SELECT * FROM r; ``` 注意:这个查询会包括id=12的节点,以及它的所有父节点。如果不想包括自身,可以在最后加上`WHERE id <> 12`。 例子2:查询id为1的所有子节点(包括自身?同样,递归基中选择了id=1,所以包括自身,如果不想要,同样可以过滤) ```sql WITH RECURSIVE r AS ( -- 递归基:从id为1的记录开始 SELECT id, parent_id, name FROM category WHERE id = 1 UNION ALL -- 递归步:找到r中所有记录的子节点(注意连接方向) SELECT c.id, c.parent_id, c.name FROM category c INNER JOIN r ON c.parent_id = r.id ) SELECT * FROM r; ``` 这里递归步的连接条件是`c.parent_id = r.id`,表示找r中每个节点的直接子节点。 注意事项: 1. 递归深度:MySQL默认限制了递归深度(通过系统变量`cte_max_recursion_depth`,默认1000),如果递归层数超过这个值,会报错。可以通过设置会话变量来调整,例如:`SET SESSION cte_max_recursion_depth = 10000;`。 2. 避免无限循环:在递归查询中,如果数据有循环(比如A的父节点是B,而B的父节点又是A),则递归会一直进行直到达到最大深度。为了避免这种情况,可以在递归步中加入条件来终止循环(例如,通过记录路径来检是否重复)。 引用[3]中展示了一种不使用`WITH RECURSIVE`的递归查询方法,它使用了变量和GROUP_CONCAT函数,但这种方法较为复杂,且依赖于特定的变量赋值顺序,在MySQL 8中不推荐使用。 因此,我们推荐使用MySQL 8的`WITH RECURSIVE`语法来实现递归查询。 生成相关问题: 1. 如何避免递归查询中的无限循环? 2. 递归查询的性能如何优化? 3. 除了递归查询,还有其他方法处理树形结构数据吗? </think> ### MySQL 8递归查询的实现方法 MySQL 8通过`WITH RECURSIVE`语法实现递归查询,主要用于处理树形结构数据(如菜单、地区、组织架构等)。以下是具体实现方法和示例: --- #### 1. **基本语法结构** ```sql WITH RECURSIVE 临时表名 AS ( -- 初始查询递归起点) SELECT ... UNION ALL -- 递归查询(连接父/子节点) SELECT ... FROM 表名 JOIN 临时表名 ON 连接条件 ) SELECT * FROM 临时表名; ``` --- #### 2. **两种典型场景** ##### **场景1:查询所有子节点(自上而下)** **示例**:查询ID=1的所有子孙节点(含自身) ```sql WITH RECURSIVE sub_tree AS ( -- 初始查询:起点ID=1 SELECT id, name, parent_id FROM category WHERE id = 1 UNION ALL -- 递归查询:连接子节点 SELECT c.id, c.name, c.parent_id FROM category c JOIN sub_tree st ON c.parent_id = st.id ) SELECT * FROM sub_tree; ``` **说明**: - 从根节点`id=1`开始 - 递归步通过`c.parent_id = st.id`向下找子节点 ##### **场景2:查询所有父节点(自下而上)** **示例**:查询ID=12的所有祖先节点(含自身) ```sql WITH RECURSIVE parent_tree AS ( -- 初始查询:起点ID=12 SELECT id, name, parent_id FROM category WHERE id = 12 UNION ALL -- 递归查询:连接父节点 SELECT c.id, c.name, c.parent_id FROM category c JOIN parent_tree pt ON pt.parent_id = c.id ) SELECT * FROM parent_tree; ``` **说明**: - 从叶节点`id=12`开始 - 递归步通过`pt.parent_id = c.id`向上找父节点[^1][^2] --- #### 3. **关键注意事项** 1. **终止条件**: - 隐式终止:当递归步不再产生新结果时自动停止 - 显式终止:添加终止条件(如`WHERE level < 5`限制深度) 2. **递归深度限制**: - 默认最大递归深度为`1000` - 修改深度:`SET SESSION cte_max_recursion_depth = 10000;` 3. **避免循环引用**: - 若数据存在环(如A→B→A),需添加路径检: ```sql AND c.id NOT IN (SELECT id FROM parent_tree) -- 防止重复 ``` --- #### 4. **性能优化建议** - **索引优化**:确保`parent_id`字段有索引 - **限制字段**:仅选择必要字段(避免`SELECT *`) - **深度控制**:通过`WHERE level <= N`限制递归层数[^2] --- #### 5. **与旧版本对比** | 方法 | MySQL 5.x及之前 | MySQL 8 | |---------------------|----------------------|----------------------| | **实现方式** | 存储过程/函数 | `WITH RECURSIVE` | | **可读性** | 复杂 | 简洁直观 | | **执行效率** | 较低 | 更高 | | **维护难度** | 高 | 低 | > ⚠️ 旧版本需用存储过程实现递归,代码复杂且效率低下[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值