MySQL - 递归列出所有 parents 和 table 中所有项目的祖先
MySQL - Recursively list all parents and ancestors of all items in table
我有一个 table,其中 parent/child 层次结构支持多级(理论上无限)嵌套:
|------|-------------------|-------------|
| id | title | parent_id |
|------|-------------------|-------------|
| 1 | Dashboard | 0 |
| 2 | Content | 0 |
| 3 | Modules | 0 |
| 17 | User Modules | 3 |
| 31 | Categories | 17 |
| ... | | |
|------|-------------------|-------------|
我正在尝试构建一个查询,该查询生成每个项目的 parent 项目的串联列表,直到树中最高的 parent:
|------|----------------------|
| id | concatenatedParents |
|------|----------------------|
| 1 | 0 |
| 2 | 0 |
| 3 | 0 |
| 17 | 3,0 |
| 31 | 17,3,0 |
| ... | |
|------|----------------------|
根据此处的许多其他答案,我构建了以下 MySQL 查询:
SELECT parentsTable._id, GROUP_CONCAT(parentsTable.parent_id SEPARATOR ',') as concatenatedParents FROM (
SELECT
@r AS _id,
(SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id,
@l := @l + 1 AS lvl
FROM
(SELECT @r := 31, @l := 0) vars,
menu m
WHERE @r <> 0
) as parentsTable
请参阅此处 Fiddle:http://sqlfiddle.com/#!9/48d276f/902/0
但是此查询仅适用于一个给定的 child id(本例中为 31)。我没有成功扩展整个 table 的查询,有什么方法可以重置 table 中每一行的计数器变量吗?
我看到许多建议使用固定数量的连接的答案,但接受可变数量级别的解决方案会更可取。
在 MySQL 8 中,这是可能的,这要归功于递归查询(谢谢@GMB),但由于我们仍然 运行 在 MySQL 5.7 中,如果存在解决方案,我很感兴趣也适用于旧版本。
如果你是运行 MySQL 8.0,这个最好用递归查询解决:
with recursive cte as (
select id, parent_id, 1 lvl from mytable
union all
select c.id, t.parent_id, lvl + 1
from cte c
inner join mytable t on t.id = c.parent_id
)
select id, group_concat(parent_id order by lvl) all_parents
from cte
group by id
id | all_parents
-: | :----------
1 | 0
2 | 0
3 | 0
17 | 3,0
31 | 17,3,0
CREATE PROCEDURE make_csv_parent ()
BEGIN
CREATE TABLE temp ( id INT PRIMARY KEY, parent_id INT, parents TEXT);
INSERT INTO temp (id, parent_id, parents)
SELECT id, parent_id, parent_id
FROM menu
WHERE parent_id = 0;
WHILE ROW_COUNT() DO
INSERT IGNORE INTO temp (id, parent_id, parents)
SELECT menu.id, menu.parent_id, CONCAT(menu.parent_id, ',', temp.parents)
FROM menu
JOIN temp ON menu.parent_id = temp.id;
END WHILE;
SELECT id, parents FROM temp;
DROP TABLE temp;
END
我有一个 table,其中 parent/child 层次结构支持多级(理论上无限)嵌套:
|------|-------------------|-------------|
| id | title | parent_id |
|------|-------------------|-------------|
| 1 | Dashboard | 0 |
| 2 | Content | 0 |
| 3 | Modules | 0 |
| 17 | User Modules | 3 |
| 31 | Categories | 17 |
| ... | | |
|------|-------------------|-------------|
我正在尝试构建一个查询,该查询生成每个项目的 parent 项目的串联列表,直到树中最高的 parent:
|------|----------------------|
| id | concatenatedParents |
|------|----------------------|
| 1 | 0 |
| 2 | 0 |
| 3 | 0 |
| 17 | 3,0 |
| 31 | 17,3,0 |
| ... | |
|------|----------------------|
根据此处的许多其他答案,我构建了以下 MySQL 查询:
SELECT parentsTable._id, GROUP_CONCAT(parentsTable.parent_id SEPARATOR ',') as concatenatedParents FROM (
SELECT
@r AS _id,
(SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id,
@l := @l + 1 AS lvl
FROM
(SELECT @r := 31, @l := 0) vars,
menu m
WHERE @r <> 0
) as parentsTable
请参阅此处 Fiddle:http://sqlfiddle.com/#!9/48d276f/902/0
但是此查询仅适用于一个给定的 child id(本例中为 31)。我没有成功扩展整个 table 的查询,有什么方法可以重置 table 中每一行的计数器变量吗?
我看到许多建议使用固定数量的连接的答案,但接受可变数量级别的解决方案会更可取。
在 MySQL 8 中,这是可能的,这要归功于递归查询(谢谢@GMB),但由于我们仍然 运行 在 MySQL 5.7 中,如果存在解决方案,我很感兴趣也适用于旧版本。
如果你是运行 MySQL 8.0,这个最好用递归查询解决:
with recursive cte as (
select id, parent_id, 1 lvl from mytable
union all
select c.id, t.parent_id, lvl + 1
from cte c
inner join mytable t on t.id = c.parent_id
)
select id, group_concat(parent_id order by lvl) all_parents
from cte
group by id
id | all_parents -: | :---------- 1 | 0 2 | 0 3 | 0 17 | 3,0 31 | 17,3,0
CREATE PROCEDURE make_csv_parent ()
BEGIN
CREATE TABLE temp ( id INT PRIMARY KEY, parent_id INT, parents TEXT);
INSERT INTO temp (id, parent_id, parents)
SELECT id, parent_id, parent_id
FROM menu
WHERE parent_id = 0;
WHILE ROW_COUNT() DO
INSERT IGNORE INTO temp (id, parent_id, parents)
SELECT menu.id, menu.parent_id, CONCAT(menu.parent_id, ',', temp.parents)
FROM menu
JOIN temp ON menu.parent_id = temp.id;
END WHILE;
SELECT id, parents FROM temp;
DROP TABLE temp;
END