如何创建非自引用 MySQL 递归查询
How to create a non-self-referencing MySQL recursive query
这里有一个相关问题:SO - Mysql Recursive Query
我正在尝试确定如何在 mysql(版本>=8)中创建递归查询,它可以收集所有以某种方式 'linked' 到原始行的行。结构比较简单:
CREATE TABLE link (
id INT,
left_id INT,
right_id INT
);
CREATE TABLE item (
id INT,
content VARCHAR(100)
);
INSERT INTO item (id, content) VALUES (1, 'first linked item');
INSERT INTO item (id, content) VALUES (2, 'unlinked item');
INSERT INTO item (id, content) VALUES (3, 'second linked item');
INSERT INTO item (id, content) VALUES (4, 'third linked item');
INSERT INTO item (id, content) VALUES (5, 'fourth linked item');
INSERT INTO item (id, content) VALUES (6, 'second group item 1');
INSERT INTO item (id, content) VALUES (7, 'second group item 2');
INSERT INTO link (id, left_id, right_id) VALUES (1, 1, 3);
INSERT INTO link (id, left_id, right_id) VALUES (2, 4, 3);
INSERT INTO link (id, left_id, right_id) VALUES (2, 5, 1);
INSERT INTO link (id, left_id, right_id) VALUES (4, 6, 7);
这里我们创建了 "item" 行。稍后我们可以通过向 "link" table 添加一行来决定任意 link 一项到另一项。 "left_id" 和 "right_id" 只是一个项目和另一个项目的id。这意味着 link 完成一项作为左侧或右侧没有什么不同。
查询应从一个或多个指定项目开始,找到 links 匹配 left_id 或 right_id 并将项目添加到结果中,然后对于这些新行,检查更多 link 等,直到还剩 none。
对于上面提供的测试数据,我预计:
Calling query with condition "content = 'first linked item'":
Result:
1 | 'first linked item'
3 | 'second linked item'
4 | 'third linked item'
5 | 'fourth linked item'
Calling query with condition "content = 'second group item 2'":
Result:
6 | 'second group item 1'
7 | 'second group item 2'
mysql8+ 中有哪些功能可以有效地调用此类查询?
您可以使用递归 CTE 遍历图表。例如:
with
recursive i as (
select id, content, -1 as source
from item where content = 'first linked item'
union all
select m.id, m.content, i.id
from i
join link l on l.left_id = i.id or l.right_id = i.id
join item m on m.id <> i.id and m.id <> i.source and m.id = l.left_id
or m.id <> i.id and m.id <> i.source and m.id = l.right_id
)
select id, content from i;
然后,只需更改第 3 行中的搜索条件,从不同的项目开始。
只要图形没有循环,这个解决方案就可以很好地工作。
编辑:处理循环的解决方案
根据要求,这是适用于循环的解决方案:
with
recursive i as (
select id, content, concat(',', id, ',') as route
from item where content = 'first linked item'
union all
select m.id, m.content, concat(i.route, m.id, ',')
from i
join link l on l.left_id = i.id or l.right_id = i.id
join item m on i.route not like concat('%,', m.id, ',%')
and (m.id = l.left_id or m.id = l.right_id)
)
select distinct id, content from i;
参见 DB Fiddle 我在其中介绍了一个循环。
这里有一个相关问题:SO - Mysql Recursive Query
我正在尝试确定如何在 mysql(版本>=8)中创建递归查询,它可以收集所有以某种方式 'linked' 到原始行的行。结构比较简单:
CREATE TABLE link (
id INT,
left_id INT,
right_id INT
);
CREATE TABLE item (
id INT,
content VARCHAR(100)
);
INSERT INTO item (id, content) VALUES (1, 'first linked item');
INSERT INTO item (id, content) VALUES (2, 'unlinked item');
INSERT INTO item (id, content) VALUES (3, 'second linked item');
INSERT INTO item (id, content) VALUES (4, 'third linked item');
INSERT INTO item (id, content) VALUES (5, 'fourth linked item');
INSERT INTO item (id, content) VALUES (6, 'second group item 1');
INSERT INTO item (id, content) VALUES (7, 'second group item 2');
INSERT INTO link (id, left_id, right_id) VALUES (1, 1, 3);
INSERT INTO link (id, left_id, right_id) VALUES (2, 4, 3);
INSERT INTO link (id, left_id, right_id) VALUES (2, 5, 1);
INSERT INTO link (id, left_id, right_id) VALUES (4, 6, 7);
这里我们创建了 "item" 行。稍后我们可以通过向 "link" table 添加一行来决定任意 link 一项到另一项。 "left_id" 和 "right_id" 只是一个项目和另一个项目的id。这意味着 link 完成一项作为左侧或右侧没有什么不同。
查询应从一个或多个指定项目开始,找到 links 匹配 left_id 或 right_id 并将项目添加到结果中,然后对于这些新行,检查更多 link 等,直到还剩 none。
对于上面提供的测试数据,我预计:
Calling query with condition "content = 'first linked item'":
Result:
1 | 'first linked item'
3 | 'second linked item'
4 | 'third linked item'
5 | 'fourth linked item'
Calling query with condition "content = 'second group item 2'":
Result:
6 | 'second group item 1'
7 | 'second group item 2'
mysql8+ 中有哪些功能可以有效地调用此类查询?
您可以使用递归 CTE 遍历图表。例如:
with
recursive i as (
select id, content, -1 as source
from item where content = 'first linked item'
union all
select m.id, m.content, i.id
from i
join link l on l.left_id = i.id or l.right_id = i.id
join item m on m.id <> i.id and m.id <> i.source and m.id = l.left_id
or m.id <> i.id and m.id <> i.source and m.id = l.right_id
)
select id, content from i;
然后,只需更改第 3 行中的搜索条件,从不同的项目开始。
只要图形没有循环,这个解决方案就可以很好地工作。
编辑:处理循环的解决方案
根据要求,这是适用于循环的解决方案:
with
recursive i as (
select id, content, concat(',', id, ',') as route
from item where content = 'first linked item'
union all
select m.id, m.content, concat(i.route, m.id, ',')
from i
join link l on l.left_id = i.id or l.right_id = i.id
join item m on i.route not like concat('%,', m.id, ',%')
and (m.id = l.left_id or m.id = l.right_id)
)
select distinct id, content from i;
参见 DB Fiddle 我在其中介绍了一个循环。