如何在MySQL查询中动态添加INNER JOIN实现版本历史递归查询
MySQL 动态查询版本链解决方案
你当前写死INNER JOIN的方式只能适配固定长度的版本历史,针对任意长度的版本链需求,根据你使用的MySQL版本可以选择以下两种方案:
方案1:MySQL 8.0+ 递归CTE(推荐)
MySQL 8.0开始支持的递归公共表表达式可以直接遍历自关联的树形结构,不需要手动拼接JOIN语句,自动适配任意长度的版本链。
行式输出(更通用,无需调整代码)
如果可以接受每个版本占一行的输出格式,直接使用以下语句即可查询任意版本的完整历史,只需修改锚点查询的WHERE id = 12为你要查询的最新版本id即可:
WITH RECURSIVE version_chain AS ( -- 锚点:先定位到要查询的最末端版本 SELECT id, first, ik_next, 1 AS version_index FROM first_next WHERE id = 12 UNION ALL -- 递归:每次关联上一个版本 SELECT fn.id, fn.first, fn.ik_next, vc.version_index + 1 FROM first_next fn INNER JOIN version_chain vc ON fn.id = vc.ik_next ) -- 按版本先后排序输出 SELECT id, first AS version_name, version_index FROM version_chain ORDER BY version_index DESC;
列式输出(和你原有查询输出格式一致)
如果需要和你原写法一样,每个版本作为单独的列输出,可以结合动态SQL自动生成对应列:
SET @query_sql = NULL; -- 先获取版本链的所有节点 WITH RECURSIVE version_chain AS ( SELECT id, first, ik_next, 1 AS version_index FROM first_next WHERE id = 12 UNION ALL SELECT fn.id, fn.first, fn.ik_next, vc.version_index + 1 FROM first_next fn INNER JOIN version_chain vc ON fn.id = vc.ik_next ) -- 动态拼接每一列的查询逻辑 SELECT GROUP_CONCAT( CONCAT( 'MAX(IF(version_index = ', version_index, ', id, NULL)) AS id_v', version_index, ', ', 'MAX(IF(version_index = ', version_index, ', version_name, NULL)) AS name_v', version_index ) ORDER BY version_index DESC ) INTO @query_sql FROM (SELECT id, first AS version_name, version_index FROM version_chain) t; -- 拼接完整查询语句 SET @query_sql = CONCAT(' WITH RECURSIVE version_chain AS ( SELECT id, first, ik_next, 1 AS version_index FROM first_next WHERE id = 12 UNION ALL SELECT fn.id, fn.first, fn.ik_next, vc.version_index + 1 FROM first_next fn INNER JOIN version_chain vc ON fn.id = vc.ik_next ) SELECT ', @query_sql, ' FROM (SELECT id, first AS version_name, version_index FROM version_chain) t; '); -- 执行动态查询 PREPARE stmt FROM @query_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
方案2:MySQL 5.x 兼容方案
如果你使用的是不支持CTE的低版本MySQL,可以使用用户变量迭代的方式遍历版本链:
SELECT @id := id AS id, first AS version_name, @level := @level + 1 AS version_index FROM first_next, (SELECT @id := 12, @level := 0) init WHERE FIND_IN_SET(id, @id) AND LENGTH(@id := CONCAT(@id, ',', ik_next)) > 0 ORDER BY version_index DESC;
内容的提问来源于stack exchange,提问作者eboulme62980.fr
相关产品推荐
相关产品推荐

