You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL菜单树形结构查询异常:现有查询返回重复行不符预期

我来帮你搞定这个菜单树形结构的查询问题!你的原查询之所以返回重复行,是因为关联条件写错了——node.parentid = parent.parentid会把所有拥有相同父ID的节点互相匹配,完全没按真正的父子层级关联,自然会出现大量重复。

下面给出两种靠谱的解决方案,优先推荐第一种(MySQL 8.0+适用),简洁又好维护:

解决方案1:使用递归CTE(MySQL 8.0+ 推荐)

MySQL 8.0及以上支持递归公共表达式(CTE),这是处理树形结构数据的最优方案。我们可以通过递归遍历所有层级的菜单,同时生成对应的缩进格式:

WITH RECURSIVE menu_tree AS (
    -- 第一步:获取所有根节点(parentid=0),初始化层级为0
    SELECT 
        id, 
        menuname, 
        parentid, 
        0 AS level,
        CONCAT('>', menuname) AS menu_display
    FROM menu
    WHERE parentid = 0
    UNION ALL
    -- 第二步:递归遍历子节点,层级递增,生成对应缩进
    SELECT 
        node.id, 
        node.menuname, 
        node.parentid, 
        parent.level + 1 AS level,
        -- 根据层级生成缩进符号,每深一层多一个-->
        CONCAT(REPEAT('-->', parent.level + 1), node.menuname) AS menu_display
    FROM menu AS node
    -- 核心关联:子节点的parentid等于父节点的id
    JOIN menu_tree AS parent ON node.parentid = parent.id
)
-- 输出最终的菜单展示,按id排序保持顺序
SELECT menu_display FROM menu_tree ORDER BY id;

执行后会得到符合父子层级的树形结构(修正了预期排版的层级细节,更贴合实际数据关系):

>dashboard
>Content
-->Home Page Content
---->Block Content
---->Site Content
-->Banners
>Settings

解决方案2:使用存储过程(兼容MySQL 5.x)

如果你还在使用MySQL 5.x版本(不支持CTE),可以用递归存储过程来实现:

DELIMITER //
CREATE PROCEDURE get_menu_tree(IN parent_id INT, IN indent VARCHAR(255))
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE child_id INT;
    DECLARE child_name VARCHAR(255);
    -- 定义游标遍历当前父节点的所有子节点
    DECLARE cur CURSOR FOR SELECT id, menuname FROM menu WHERE parentid = parent_id;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN cur;
    read_loop: LOOP
        FETCH cur INTO child_id, child_name;
        IF done THEN
            LEAVE read_loop;
        END IF;
        -- 输出当前节点的格式化内容
        SELECT CONCAT(indent, '>', child_name) AS menu_display;
        -- 递归调用,处理当前节点的子节点,缩进增加
        CALL get_menu_tree(child_id, CONCAT(indent, '--'));
    END LOOP;
    CLOSE cur;
END //
DELIMITER ;

-- 调用存储过程,从根节点(parent_id=0)开始查询
CALL get_menu_tree(0, '');

为什么原查询不行?

再帮你复盘下原查询的问题:SELECT node.id, node.name FROM menu AS node, menu AS parent WHERE node.parentid = parent.parentid 这个关联条件会把所有父ID相同的节点两两匹配,比如parentid=2的节点(Home Page Content、Banners)会和所有parentid=2的父节点重复关联,导致返回重复行。正确的关联应该是子节点的parentid等于父节点的id,但即使改了这个条件,也只能查询到两层数据,无法处理多层嵌套的树形结构,所以必须用递归的方式。

内容的提问来源于stack exchange,提问作者Niladri Banerjee - Uttarpara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:21:46