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.
中执行此操作
这里是 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 - +
我有一个 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.
中执行此操作这里是 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 - +