MySQL8 查询一个等级的全血统(祖先+后代)

MySQL8 query whole bloodline (ancestors + descendants) of a hierarchy

MySQL8 允许使用 with 语法来查询层次结构。例如,查询一个项目的所有祖先。反过来也可以递归查询所有后代。

我正在寻找结合了这两个方面的查询:所有后代、祖先的查询,最好是项目自身(请参阅包含的图形,所有彩色项目)。其实也可以说,无所有祖先兄弟姐妹的所有物品。我想我们可以说“血统”。可以只 union 这两个结果,但我需要一个没有联合的解决方案。

这可能吗?我欢迎任何方法。

作为一个概览,我的工会尝试。注意,目前有一个错误,有些 parent_id 是错误的;我正在努力)

(with recursive cte as (
    SELECT id, parent_id
    from categories
    where id = 'C2'
    union all
    select t.parent_id, cte.id
    from cte
             inner join categories t on t.id = cte.parent_id
    where cte.id is not null
)
 select id, parent_id
 from cte
 where cte.id is not null)

union

(with recursive cte as (
    SELECT id, parent_id
    from categories
    where id = 'C2'
    union all
    select t.id, cte.parent_id
    from cte
             inner join categories t on t.parent_id = cte.id
)
 select id, parent_id
 from cte);

以及给定图表的数据。

INSERT INTO categories (id, parent_id) VALUES ('A1', null);
INSERT INTO categories (id, parent_id) VALUES ('B1', 'A1');
INSERT INTO categories (id, parent_id) VALUES ('B2', 'A1');
INSERT INTO categories (id, parent_id) VALUES ('B3', 'A1');
INSERT INTO categories (id, parent_id) VALUES ('C1', 'B2');
INSERT INTO categories (id, parent_id) VALUES ('C2', 'B2');
INSERT INTO categories (id, parent_id) VALUES ('D1', 'C1');
INSERT INTO categories (id, parent_id) VALUES ('D2', 'C1');
INSERT INTO categories (id, parent_id) VALUES ('D3', 'C2');
INSERT INTO categories (id, parent_id) VALUES ('D4', 'C2');
INSERT INTO categories (id, parent_id) VALUES ('D5', 'C2');
INSERT INTO categories (id, parent_id) VALUES ('E1', 'D5');

预期输出

id,parent_id
C2,B2
A1,null
B2,A1
D3,C2
D4,C2
D5,C2
E1,D5

如果您在 ON 子句中包含祖先和后代的两个条件,则可以使用 1 个递归 CTE 来完成。
您将需要另一列 type,它指示每一行是针对祖先还是后代:

WITH RECURSIVE cte AS (
    SELECT id, parent_id, 0 type
    FROM categories
    WHERE id = 'C2'
    UNION ALL
    SELECT t.id, t.parent_id,
           CASE WHEN c.parent_id = t.id THEN -1 ELSE 1 END
    FROM categories t INNER JOIN cte c
    ON c.parent_id = t.id OR c.id = t.parent_id
    WHERE (type = 0) 
       OR (c.parent_id = t.id AND type = -1) 
       OR (c.id = t.parent_id AND type = 1)
)
SELECT id, parent_id FROM cte

参见demo