'SQL recursion on a self-referencing table to obtain specific order
I have a table as below:
| 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 |
I'm sure you can already see the pattern - each Page can have any number of "SubPages" defined by the ParentPageId
I've been trying to get a query that can produce the following ordered output (without using the LinkSlug alphabetical ordering because they can be anything):
| 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 |
I tried doing some self-joins and grouping but ended up with only one level of recursion so it was no good for the 3rd and potentially nth degree sub pages, and then also tried using a CTE as I understand they're good for recursive queries but somehow ended up producing the same table I started with and am now at a loss!
The more I try the worse it gets - I know I need to effectively select top levels (with null ParentPageId) ordered by [Order], then inject wherever there's sub pages ordering by [Order], and repeat until there are no children left - but no idea how to do this in SQL.
And here's the fiddle script just in case:
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);
Solution 1:[1]
You can try this:
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;
Note: that in MS SQL you need to use concat instead of ||; in MySQL - +
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Sve Kamenska |

