SQL 对 self-referencing table 的递归以获得特定顺序

SQL recursion on a self-referencing table to obtain specific order

我有一个 table 如下:

Id LinkSlug ParentPageId Order
1 home 0
2 page2 1
3 page3 2
4 page11 1 0
5 page12 1 1
6 page13 1 2
7 page21 2 0
8 page22 2 1
9 page121 5 0
10 page122 5 1
11 page123 5 2

我相信您已经看到了这种模式 - 每个 Page 可以有任意数量的“子页面”,由 ParentPageId

定义

我一直在尝试获得一个可以产生以下有序输出的查询(不使用 LinkSlug 字母顺序,因为它们可以是任何东西):

Id LinkSlug ParentPageId Order
1 home 0
4 page11 1 0
5 page12 1 1
9 page121 5 0
10 page122 5 1
11 page123 5 2
6 page13 1 2
2 page2 1
7 page21 2 0
8 page22 2 1
3 page3 2

我尝试做一些 self-joins 和分组,但最终只有一级递归,所以它对第 3 级和可能的第 n 级子页面没有好处,然后我也尝试使用 CTE,据我所知它们适用于递归查询,但不知何故最终产生了与我开始时相同的 table,现在我不知所措!

我尝试的越多,情况就越糟 - 我知道我需要有效地 select 按 [Order] 排序的顶级(具有 null ParentPageId),然后在有按 [Order] 排序的子页面的地方注入,并重复直到没有 children 剩余 - 但不知道如何在 SQL.

中执行此操作

View on DB Fiddle

这里是 fiddle 脚本以防万一:

CREATE TABLE [Pages](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [LinkSlug] [nvarchar](450) NOT NULL,
    [ParentPageId] [int] NULL,
    [Order] [int] NOT NULL
);

INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (1, 'home', NULL, 0);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (2, 'page2', NULL, 1);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (3, 'page3', NULL, 2);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (4, 'page11', 1, 0);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (5, 'page12', 1, 1);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (6, 'page13', 1, 2);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (7, 'page21', 2, 0);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (8, 'page22', 2, 1);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (9, 'page121', 5, 0);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (10, 'page122', 5, 1);
INSERT into [Pages] ([Id], [LinkSlug], [ParentPageId], [Order]) VALUES (11, 'page123', 5, 2);

您需要递归 CTE 来构建层次结构并维护序列

例子

with cte1 as (
      Select [Id]
            ,[LinkSlug]
            ,[ParentPageId]
            ,[Order]
            ,Seq  = cast(10000+Row_Number() over (Order by [Order]) as varchar(500))
      From   pages
      Where  [ParentPageId] is null
      Union  All
      Select cte2.[Id]
            ,cte2.[LinkSlug]
            ,cte2.[ParentPageId]
            ,cte2.[Order]
            ,Seq  = cast(concat(cte1.Seq,'.',10000+Row_Number() over (Order by cte2.[Order])) as varchar(500))
      From   pages cte2 
      Join   cte1 on cte2.[ParentPageId] = cte1.[Id])
Select *
 From cte1
 Order By Seq

结果

你可以试试这个:

WITH cte_org (n, id, 
    LinkSlug,
    ParentPageId) AS (
SELECT
    CAST([order] as CHAR(200)),
    id, 
    LinkSlug,
    ParentPageId
FROM       
    pages
WHERE ParentPageId IS NULL
UNION ALL
SELECT 
    o.n || '_' || e.[order],
    e.id,
    e.LinkSlug,
    e.ParentPageId
FROM 
    pages e
    INNER JOIN cte_org o 
        ON o.id = e.ParentPageId)

SELECT * FROM cte_org order by n;

注意:在 MS SQL 中您需要使用 concat 而不是 ||;在 MySQL - +