MySQL 递归 CTE 分层路径
MySQL Recursive CTE hierarchical path
我有 categories
table 其中 parent_id
是自引用 foreign key
.
类别table:
select * from categories;
category_id
name
parent_id
1
T1
null
2
T2
null
3
T11
1
4
T12
1
5
T111
3
6
T112
3
7
T1121
6
8
T1122
6
9
T121
4
10
T122
4
11
T21
2
12
T211
11
13
T212
11
14
T2111
12
预期输出 -
select * , 'some path sql' as path from categories;
category_id
name
parent_id
path
1
T1
null
T1
2
T2
null
T2
3
T11
1
T1 -> T11
4
T12
1
T1 -> T12
5
T111
3
T1 -> T11 -> T111
6
T112
3
T1 -> T11 -> T112
7
T1121
6
T1 -> T11 -> T112 -> T1121
8
T1122
6
T1 -> T11 -> T112 -> T1122
9
T121
4
T1 -> T12 -> T121
10
T122
4
T1 -> T12 -> T122
11
T21
2
T2 -> T21
12
T211
11
T1 -> T21 -> T211
13
T212
11
T1 -> T21 -> T212
14
T2111
12
T1 -> T21 -> T211 -> T2111
我试过的:
我试过这个查询,但它有 return 38 行。我想要实际的 14 行。
WITH RECURSIVE cte_name AS (
select category_id, name, parent_id , name AS path from categories
UNION ALL
select c.category_id,c.name,c.parent_id, concat_ws(' -> ', cte_name.path, c.name)
from categories as c
INNER JOIN cte_name ON cte_name.category_id = c.parent_id
)
SELECT * FROM cte_name;
我不知道如何编写确切的查询来 return 实际所有 14 行及其路径。
您可以在 CTE 中使用 CONCAT(parent_path,separator,child_id)
来构建路径。您需要在 UNION 之前的 CTE 的第一部分中指定“WHERE parent_id IS NULL”,以便我们仅包含从根开始的路径。
CREATE TABLE IF NOT EXISTS categories (
category_id int(11) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
parent_id int(11) DEFAULT NULL,
created_at timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (category_id),
FOREIGN KEY (parent_id) REFERENCES categories(category_id) ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T1', null);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T2', null);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T11', 1);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T12', 1);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T111', 3);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T112', 3);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T1121', 6);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T1122', 6);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T121', 4);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T122', 4);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T21', 2);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T211', 11);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T212', 11);
INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T2111', 12);
WITH RECURSIVE cte_name AS (
select category_id, name, parent_id , name AS path from categories
where parent_id is null
UNION ALL
select c.category_id,c.name,c.parent_id, concat_ws(' -> ', cte_name.path, c.name)
from categories as c
INNER JOIN cte_name ON cte_name.category_id = c.parent_id
)
SELECT * FROM cte_name;
category_id | name | parent_id | path
----------: | :---- | --------: | :-------------------------
1 | T1 | null | T1
2 | T2 | null | T2
3 | T11 | 1 | T1 -> T11
4 | T12 | 1 | T1 -> T12
11 | T21 | 2 | T2 -> T21
5 | T111 | 3 | T1 -> T11 -> T111
6 | T112 | 3 | T1 -> T11 -> T112
9 | T121 | 4 | T1 -> T12 -> T121
10 | T122 | 4 | T1 -> T12 -> T122
12 | T211 | 11 | T2 -> T21 -> T211
13 | T212 | 11 | T2 -> T21 -> T212
7 | T1121 | 6 | T1 -> T11 -> T112 -> T1121
8 | T1122 | 6 | T1 -> T11 -> T112 -> T1122
14 | T2111 | 12 | T2 -> T21 -> T211 -> T2111
db<>fiddle here
我有 categories
table 其中 parent_id
是自引用 foreign key
.
类别table:
select * from categories;
category_id | name | parent_id |
---|---|---|
1 | T1 | null |
2 | T2 | null |
3 | T11 | 1 |
4 | T12 | 1 |
5 | T111 | 3 |
6 | T112 | 3 |
7 | T1121 | 6 |
8 | T1122 | 6 |
9 | T121 | 4 |
10 | T122 | 4 |
11 | T21 | 2 |
12 | T211 | 11 |
13 | T212 | 11 |
14 | T2111 | 12 |
预期输出 -
select * , 'some path sql' as path from categories;
category_id | name | parent_id | path |
---|---|---|---|
1 | T1 | null | T1 |
2 | T2 | null | T2 |
3 | T11 | 1 | T1 -> T11 |
4 | T12 | 1 | T1 -> T12 |
5 | T111 | 3 | T1 -> T11 -> T111 |
6 | T112 | 3 | T1 -> T11 -> T112 |
7 | T1121 | 6 | T1 -> T11 -> T112 -> T1121 |
8 | T1122 | 6 | T1 -> T11 -> T112 -> T1122 |
9 | T121 | 4 | T1 -> T12 -> T121 |
10 | T122 | 4 | T1 -> T12 -> T122 |
11 | T21 | 2 | T2 -> T21 |
12 | T211 | 11 | T1 -> T21 -> T211 |
13 | T212 | 11 | T1 -> T21 -> T212 |
14 | T2111 | 12 | T1 -> T21 -> T211 -> T2111 |
我试过的:
我试过这个查询,但它有 return 38 行。我想要实际的 14 行。
WITH RECURSIVE cte_name AS (
select category_id, name, parent_id , name AS path from categories
UNION ALL
select c.category_id,c.name,c.parent_id, concat_ws(' -> ', cte_name.path, c.name)
from categories as c
INNER JOIN cte_name ON cte_name.category_id = c.parent_id
)
SELECT * FROM cte_name;
我不知道如何编写确切的查询来 return 实际所有 14 行及其路径。
您可以在 CTE 中使用 CONCAT(parent_path,separator,child_id)
来构建路径。您需要在 UNION 之前的 CTE 的第一部分中指定“WHERE parent_id IS NULL”,以便我们仅包含从根开始的路径。
CREATE TABLE IF NOT EXISTS categories ( category_id int(11) NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL, parent_id int(11) DEFAULT NULL, created_at timestamp NOT NULL DEFAULT current_timestamp(), PRIMARY KEY (category_id), FOREIGN KEY (parent_id) REFERENCES categories(category_id) ON UPDATE CASCADE ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T1', null); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T2', null); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T11', 1); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T12', 1); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T111', 3); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T112', 3); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T1121', 6); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T1122', 6); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T121', 4); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T122', 4); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T21', 2); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T211', 11); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T212', 11); INSERT INTO categories (`name`,`parent_id`) VALUES ( 'T2111', 12);
WITH RECURSIVE cte_name AS ( select category_id, name, parent_id , name AS path from categories where parent_id is null UNION ALL select c.category_id,c.name,c.parent_id, concat_ws(' -> ', cte_name.path, c.name) from categories as c INNER JOIN cte_name ON cte_name.category_id = c.parent_id ) SELECT * FROM cte_name;
category_id | name | parent_id | path ----------: | :---- | --------: | :------------------------- 1 | T1 | null | T1 2 | T2 | null | T2 3 | T11 | 1 | T1 -> T11 4 | T12 | 1 | T1 -> T12 11 | T21 | 2 | T2 -> T21 5 | T111 | 3 | T1 -> T11 -> T111 6 | T112 | 3 | T1 -> T11 -> T112 9 | T121 | 4 | T1 -> T12 -> T121 10 | T122 | 4 | T1 -> T12 -> T122 12 | T211 | 11 | T2 -> T21 -> T211 13 | T212 | 11 | T2 -> T21 -> T212 7 | T1121 | 6 | T1 -> T11 -> T112 -> T1121 8 | T1122 | 6 | T1 -> T11 -> T112 -> T1122 14 | T2111 | 12 | T2 -> T21 -> T211 -> T2111
db<>fiddle here