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

如何在MySQL中查询用户父子关系及任意两用户的层级步数差?

嘿,我来帮你搞定这个MySQL用户父子层级步数计算的问题!

解决MySQL用户父子关系的层级步数计算问题

首先咱们先明确下你的Users表结构,用表格展示更清晰:

字段名类型说明
user_idINT (PK, AI)用户ID(主键、自增)
user_nameVARCHAR(45)用户名
parent_user_idINT (FK)父用户ID(关联user_id)

假设你的示例数据对应你提到的dave→pritesh→larry→abha路径,大概是这样:

INSERT INTO Users (user_name, parent_user_id) VALUES
('abha', NULL),   -- 根节点
('larry', 1),
('pritesh', 2),
('dave', 3),
('john', 1),
('emily', 5);

核心思路:用递归CTE遍历层级关系

MySQL 8.0及以上支持递归CTE(Common Table Expression),这是处理树形层级数据最方便的方式。我们可以通过递归生成每个用户到其所有祖先的路径和步数,再基于这个结果查询任意两用户的步数差。

步骤1:生成所有用户的层级路径与步数

先写一个递归CTE,遍历每个用户的所有祖先节点,记录从该用户到祖先的步数和完整路径:

WITH RECURSIVE UserHierarchy AS (
    -- 基础情况:每个用户自身是自己的祖先,步数为0
    SELECT 
        user_id AS descendant_id,
        user_name AS descendant_name,
        user_id AS ancestor_id,
        user_name AS ancestor_name,
        0 AS steps,
        CAST(user_name AS CHAR(255)) AS hierarchy_path
    FROM Users
    
    UNION ALL
    
    -- 递归情况:向上遍历父节点,步数累加1,路径追加父用户名
    SELECT 
        uh.descendant_id,
        uh.descendant_name,
        u.user_id AS ancestor_id,
        u.user_name AS ancestor_name,
        uh.steps + 1,
        CONCAT(uh.hierarchy_path, '→', u.user_name)
    FROM UserHierarchy uh
    JOIN Users u ON uh.ancestor_id = u.parent_user_id
)
SELECT * FROM UserHierarchy;

执行这个查询后,你会看到每条记录对应一个用户(descendant)到其某个祖先(ancestor)的步数和路径,比如dave对应的记录里会有到abha的条目,步数为3,路径是dave→pritesh→larry→abha。

步骤2:查询任意两用户的层级步数差

场景1:两用户在同一条直接路径上(比如dave和abha)

直接从上面的CTE中筛选目标用户即可:

WITH RECURSIVE UserHierarchy AS (
    SELECT 
        user_id AS descendant_id,
        user_name AS descendant_name,
        user_id AS ancestor_id,
        user_name AS ancestor_name,
        0 AS steps,
        CAST(user_name AS CHAR(255)) AS hierarchy_path
    FROM Users
    UNION ALL
    SELECT 
        uh.descendant_id,
        uh.descendant_name,
        u.user_id AS ancestor_id,
        u.user_name AS ancestor_name,
        uh.steps + 1,
        CONCAT(uh.hierarchy_path, '→', u.user_name)
    FROM UserHierarchy uh
    JOIN Users u ON uh.ancestor_id = u.parent_user_id
)
SELECT 
    descendant_name AS 用户A,
    ancestor_name AS 用户B,
    steps AS 层级步数差,
    hierarchy_path AS 层级路径
FROM UserHierarchy
WHERE descendant_name = 'dave' AND ancestor_name = 'abha';

执行结果会直接返回:用户A是dave,用户B是abha,步数差3,路径dave→pritesh→larry→abha。

场景2:两用户不在同一条直接路径上(比如dave和emily)

这时候需要先找到他们的最近共同祖先,再计算各自到共同祖先的步数之和:

WITH RECURSIVE UserHierarchy AS (
    SELECT 
        user_id AS descendant_id,
        user_name AS descendant_name,
        user_id AS ancestor_id,
        user_name AS ancestor_name,
        0 AS steps,
        CAST(user_name AS CHAR(255)) AS hierarchy_path
    FROM Users
    UNION ALL
    SELECT 
        uh.descendant_id,
        uh.descendant_name,
        u.user_id AS ancestor_id,
        u.user_name AS ancestor_name,
        uh.steps + 1,
        CONCAT(uh.hierarchy_path, '→', u.user_name)
    FROM UserHierarchy uh
    JOIN Users u ON uh.ancestor_id = u.parent_user_id
)
SELECT 
    uh1.descendant_name AS 用户1,
    uh2.descendant_name AS 用户2,
    uh1.steps + uh2.steps AS 总层级步数差,
    CONCAT(uh1.hierarchy_path, '←', REPLACE(uh2.hierarchy_path, common_ancestor.ancestor_name, '')) AS 完整路径
FROM UserHierarchy uh1
JOIN UserHierarchy uh2 ON uh1.ancestor_id = uh2.ancestor_id
-- 子查询找最近共同祖先
JOIN (
    SELECT 
        uh1.descendant_id,
        uh2.descendant_id,
        uh1.ancestor_id,
        uh1.ancestor_name
    FROM UserHierarchy uh1
    JOIN UserHierarchy uh2 ON uh1.ancestor_id = uh2.ancestor_id
    WHERE uh1.descendant_name = 'dave' AND uh2.descendant_name = 'emily'
    ORDER BY uh1.steps DESC
    LIMIT 1
) common_ancestor ON uh1.descendant_id = common_ancestor.descendant_id 
    AND uh1.ancestor_id = common_ancestor.ancestor_id 
    AND uh2.descendant_id = common_ancestor.descendant_id 
    AND uh2.ancestor_id = common_ancestor.ancestor_id;

这个查询会返回:用户1是dave,用户2是emily,总步数差5,完整路径dave→pritesh→larry→abha←john←emily。

注意事项

  • 这个方案要求MySQL版本在8.0及以上,因为递归CTE是8.0才引入的特性;
  • 如果你的parent_user_id允许为NULL(根节点),递归会自动在根节点停止,不会陷入死循环;
  • 如果存在循环引用(比如A的父是B,B的父是A),需要在递归中添加条件避免死循环,比如记录已访问的节点ID。

内容的提问来源于stack exchange,提问作者Maulik Kanani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:04:55