mysql - 查询获取带有子记录的父记录,然后是mysql中的下一个父子记录

标签 mysql sql

我在数据库的 Names 表中有以下格式的数据:

ID  | Name                     | ParentID
1   | Parent 1                 | 0
2   | Parent 2                 | 0
3   | Parent 1 Child 1         | 1
4   | Parent 2 Child 1         | 2
5   | Parent 1 Child 1 Child   | 3
6   | Parent 2 Child 1 Child 1 | 4
7   | Parent 2 Child 1 Child 2 | 4

ParentID 列将 ID 列中的数据作为父记录。 ParentID0 值表示根项目。我需要编写查询以按以下顺序获取数据:

ID  | Name                     | ParentID
1   | Parent 1                 | 0
3   | Parent 1 Child 1         | 1
5   | Parent 1 Child 1 Child   | 3
2   | Parent 2                 | 0
4   | Parent 2 Child 1         | 2    
6   | Parent 2 Child 1 Child 1 | 4
7   | Parent 2 Child 1 Child 2 | 4

我需要获取根记录(记录 ParentID 为 0),然后是该根的所有 childsub-children记录,然后获取下一个根记录,然后是该根记录的 childsub-children 等等。

最佳答案

我在这里提出的解决方案使用了物化路径的概念。以下是使用示例数据的物化路径示例。我希望它能帮助您理解物化路径的概念:

+----+--------------------------+----------+------------------+
| ID |           Name           | ParentID | MaterializedPath |
+----+--------------------------+----------+------------------+
|  1 | Parent 1                 |        0 | 1                |
|  2 | Parent 2                 |        0 | 2                |
|  4 | Parent 2 Child 1         |        2 | 2.4              |
|  6 | Parent 2 Child 1 Child 1 |        4 | 2.4.6            |
|  7 | Parent 2 Child 1 Child 2 |        4 | 2.4.7            |
|  3 | Parent 1 Child 1         |        1 | 1.3              |
|  5 | Parent 1 Child 1 Child   |        3 | 1.3.5            |
+----+--------------------------+----------+------------------+

每个节点 N 都有一个物化路径,这条路径告诉您从​​根节点到节点 N 的路径。它可以通过连接节点 ID 来构建。例如,要从根节点开始到达节点 5,您需要访问节点 1、节点 3 和节点 5,因此节点 5 物化路径为 1.3.5

巧合的是,你要找的顺序可以通过物化路径实现排序。

在前面的例子中,物化路径是用来连接字符串的,但出于多种原因我更喜欢二进制连接。

要构建物化路径,您需要以下递归 CTE:

CREATE TABLE Tree
(
    ID int NOT NULL CONSTRAINT PK_Tree PRIMARY KEY, 
    Name nvarchar(250) NOT NULL,
    ParentID int NOT NULL,
)

INSERT INTO Tree(ID, Name, ParentID) VALUES
(1, 'Parent 1', 0),
(2, 'Parent 2', 0),
(3, 'Parent 1 Child 1', 1),
(4, 'Parent 2 Child 1', 2),
(5, 'Parent 1 Child 1 Child', 3),
(6, 'Parent 2 Child 1 Child 1', 4),
(7, 'Parent 2 Child 1 Child 2', 4)

GO
WITH T AS
(
    SELECT
        N.ID, N.Name, N.ParentID, CAST(N.ID AS varbinary(512)) AS MaterializedPath
    FROM
        Tree N
    WHERE
        N.ParentID = 0

    UNION ALL

    SELECT
        N.ID, N.Name, N.ParentID, CAST( T.MaterializedPath + CAST(N.ID AS binary(4)) AS varbinary(512) ) AS MaterializedPath
    FROM
        Tree N INNER JOIN T
            ON N.ParentID = T.ID

)
SELECT *
FROM T
ORDER BY T.MaterializedPath

结果:

+----+--------------------------+----------+----------------------------+
| ID |           Name           | ParentID |      MaterializedPath      |
+----+--------------------------+----------+----------------------------+
|  1 | Parent 1                 |        0 | 0x00000001                 |
|  3 | Parent 1 Child 1         |        1 | 0x0000000100000003         |
|  5 | Parent 1 Child 1 Child   |        3 | 0x000000010000000300000005 |
|  2 | Parent 2                 |        0 | 0x00000002                 |
|  4 | Parent 2 Child 1         |        2 | 0x0000000200000004         |
|  6 | Parent 2 Child 1 Child 1 |        4 | 0x000000020000000400000006 |
|  7 | Parent 2 Child 1 Child 2 |        4 | 0x000000020000000400000007 |
+----+--------------------------+----------+----------------------------+

上述递归 CTE 从根节点开始。计算根节点的物化路径非常简单,它是节点本身的 ID。在下一次迭代中,CTE 将根节点与其子节点连接起来。子节点 CN 的具体化路径是其父节点 PN 的具体化路径和节点 CN 的 id 的串联。随后的迭代在树上向下推进一层,直到到达叶节点。

关于mysql - 查询获取带有子记录的父记录,然后是mysql中的下一个父子记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28979233/

相关文章:

sql - 通过另一个表中的列进行SQL排序

php - MYSQL, PHP 插入到数据库中的多个表

mySQL高效统计表

c# - 搜索时 SQL 查询出错

php - 使用 php Rest api 将 Html 连接到 Mysql

php - 限制php中获取数据的列

MySQL Workbench 可视化解释不起作用

c# - SqlQuery 到 [NotMapped] 字段?

MySQL 索引列完全还是一一列?

sql - 为什么我不能在 count(*) "column"中使用别名并在having 子句中引用它?