dag*_*da1 15 mysql sql hierarchical-data
我有一个文件夹表,以id,parent_id关系与自身连接:
CREATE TABLE folders (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
title nvarchar(255) NOT NULL,
parent_id int(10) unsigned DEFAULT NULL,
PRIMARY KEY (id)
);
INSERT INTO folders(id, title, parent_id) VALUES(1, 'root', null);
INSERT INTO folders(id, title, parent_id) values(2, 'one', 1);
INSERT INTO folders(id, title, parent_id) values(3, 'target', 2);
INSERT INTO folders(id, title, parent_id) values(4, 'child one', 3);
INSERT INTO folders(id, title, parent_id) values(5, 'child two', 3);
INSERT INTO folders(id, title, parent_id) values(6, 'root 2', null);
INSERT INTO folders(id, title, parent_id) values(7, 'other child one', 6);
INSERT INTO folders(id, title, parent_id) values(8, 'other child two', 6);
Run Code Online (Sandbox Code Playgroud)
我想要一个查询,以返回该记录的所有父级,直接返回到路线和任何子级。
因此,如果我要求使用文件夹,则会id=3得到以下记录:1, 2, 3, 4, 5。我被困在如何养父母的路上。
MYSQL的版本是5.7,并且没有立即升级的计划,因此遗憾的是CTE不能选择。
我已经创建了这个SQL小提琴
在MySQL 8.0中,您可以使用递归公用表表达式来解决此用例。
以下查询为您提供给定记录的父项(包括记录本身):
with recursive parent_cte (id, title, parent_id) as (
select id, title, parent_id
from folders
where id = 3
union all
select f.id, f.title, f.parent_id
from folders f
inner join parent_cte pc on f.id = pc.parent_id
)
select * from parent_cte;
Run Code Online (Sandbox Code Playgroud)
| id | 标题| parent_id | | --- | ------ | --------- | | 3 | 目标| 2 | | 2 | 一| 1 | | 1 | 根| |
这是一个稍微不同的查询,它返回给定记录的子树:
with recursive children_cte (id, title, parent_id) as (
select id, title, parent_id
from folders
where parent_id = 3
union all
select f.id, f.title, f.parent_id
from folders f
inner join children_cte cc on f.parent_id = cc.id
)
select * from children_cte;
Run Code Online (Sandbox Code Playgroud)
| id | 标题| parent_id | | --- | --------- | --------- | | 4 | 孩子一| 3 | | 5 | 小孩二| 3 |
两个查询器可以按以下方式组合:
with recursive parent_cte (id, title, parent_id) as (
select id, title, parent_id
from folders
where id = 3
union all
select f.id, f.title, f.parent_id
from folders f
inner join parent_cte pc on f.id = pc.parent_id
),
children_cte (id, title, parent_id) as (
select id, title, parent_id
from folders
where parent_id = 3
union all
select f.id, f.title, f.parent_id
from folders f
inner join children_cte cc on f.parent_id = cc.id
)
select * from parent_cte
union all select * from children_cte;
Run Code Online (Sandbox Code Playgroud)
| id | 标题| parent_id | | --- | --------- | --------- | | 3 | 目标| 2 | | 2 | 一| 1 | | 1 | 根| | | 4 | 孩子一| 3 | | 5 | 小孩二| 3 |
在你的表的设计,ID以及PARENT_ID对应于“ 邻接列表模型 ”,用于存储树。
还有另一种设计,称为“ 嵌套集模型 ”,这使得在此处执行所需的操作更加容易。
请参阅Mike Hillyer的这篇出色的文章,其中介绍了以下两种内容: 在MySQL中管理分层数据
综上所述:
该树存储在一个表中,例如:
CREATE TABLE nested_category (
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
查找从根到给定节点(此处为“ FLASH”)的路径:
SELECT parent.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'FLASH'
ORDER BY parent.lft;
Run Code Online (Sandbox Code Playgroud)
查找给定节点的所有子节点(此处为“便携式电子”):
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
nested_category AS parent,
nested_category AS sub_parent,
(
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name
ORDER BY node.lft
)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth <= 1
ORDER BY node.lft;
Run Code Online (Sandbox Code Playgroud)
重命名到文件夹表后
解决方案是:
CREATE TABLE folders (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(20) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);
INSERT INTO folders(id, title, lft, rgt) values(1, 'root', 1, 10);
INSERT INTO folders(id, title, lft, rgt) values(2, 'one', 2, 9);
INSERT INTO folders(id, title, lft, rgt) values(3, 'target', 3, 8);
INSERT INTO folders(id, title, lft, rgt) values(4, 'child one', 4, 5);
INSERT INTO folders(id, title, lft, rgt) values(5, 'child two', 6, 7);
INSERT INTO folders(id, title, lft, rgt) values(6, 'root 2', 11, 16);
INSERT INTO folders(id, title, lft, rgt) values(7, 'other child one', 12, 13);
INSERT INTO folders(id, title, lft, rgt) values(8, 'other child two', 14, 15);
Run Code Online (Sandbox Code Playgroud)
目标路径:
SELECT parent.title
FROM folders AS node,
folders AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.title = 'target'
ORDER BY parent.lft;
Run Code Online (Sandbox Code Playgroud)
目标儿童:
SELECT node.title, (COUNT(parent.title) - (sub_tree.depth + 1)) AS depth
FROM folders AS node,
folders AS parent,
folders AS sub_parent,
(
SELECT node.title, (COUNT(parent.title) - 1) AS depth
FROM folders AS node,
folders AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.title = 'target'
GROUP BY node.title
ORDER BY node.lft
)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.title = sub_tree.title
GROUP BY node.title
HAVING depth <= 1
ORDER BY node.lft;
Run Code Online (Sandbox Code Playgroud)
要在单个查询中获取所有数据,a union应该这样做。
| 归档时间: |
|
| 查看次数: |
1210 次 |
| 最近记录: |