在我的sql <8中获取树文件夹结构的父级和子级,并且没有CTE

dag*_*da1 15 mysql sql hierarchical-data

我有一个文件夹表,以idparent_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小提琴

GMB*_*GMB 6

在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 |

DB Fiddle上的演示

  • 这是一个很好的答案,但可悲的是,我使用的是笨拙的SAS,却是5.7。我可以用代码做到这一点,但是CTE之前有办法做到这一点吗?任何指针都赞赏 (4认同)

Mar*_*lff 6

在你的表的设计,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)

重命名到文件夹表后

  • TABLE nested_category-> TABLE文件夹
  • 列category_id->列ID
  • 列名->列标题

解决方案是:

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)

参见sqlfiddle

要在单个查询中获取所有数据,a union应该这样做。