我在 SQL Server 中有一个表
myTable
如下所示:
身份证 | 父母身份 | 订单 |
---|---|---|
1 | 1 | 1 |
2 | 2 | 1 |
3 | 3 | 1 |
4 | 1 | 2 |
5 | 1 | 1 |
6 | 1 | 3 |
7 | 4 | 1 |
8 | 2 | 2 |
9 | 2 | 1 |
100 | 3 | 1 |
10 | 10 | 1 |
194 | 10 | 1 |
295 | 194 | 1 |
205 | 194 | 2 |
215 | 194 | 3 |
322 | 10 | 2 |
404 | 10 | 3 |
435 | 10 | 4 |
507 | 10 | 5 |
206 | 10 | 6 |
330 | 10 | 7 |
425 | 10 | 8 |
428 | 10 | 9 |
488 | 10 | 10 |
432 | 10 | 11 |
633 | 10 | 12 |
我希望我的存储过程返回给定
ParentId
的输出,其中包含所有 Id
s 和他们的孩子按 Order
排序。
上表所需结果的示例:
测试
ParentId
= 1 应该返回:
身份证 | 父母身份 | 订单 |
---|---|---|
1 | 1 | 1 |
5 | 1 | 1 |
4 | 1 | 2 |
7 | 4 | 1 |
6 | 1 | 3 |
Test
ParentId
= 4 应该返回 - 测试即使 Id
不是它自己的 ParentId
也会产生正确顺序的血统
身份证 | 父母身份 | 订单 |
---|---|---|
4 | 1 | 2 |
7 | 4 | 1 |
测试
ParentId
= 2 应该返回:
身份证 | 父母身份 | 订单 |
---|---|---|
2 | 2 | 1 |
9 | 2 | 1 |
8 | 2 | 2 |
测试
ParentId
= 3 应该返回:
身份证 | 父母身份 | 订单 |
---|---|---|
3 | 3 | 1 |
100 | 3 | 1 |
测试
ParentId
= 10 应该返回(这是棘手的):
身份证 | 父母身份 | 订单 |
---|---|---|
10 | 10 | 1 |
194 | 10 | 1 |
295 | 194 | 1 |
205 | 194 | 2 |
215 | 194 | 3 |
322 | 10 | 2 |
404 | 10 | 3 |
435 | 10 | 4 |
507 | 10 | 5 |
206 | 10 | 6 |
330 | 10 | 7 |
425 | 10 | 8 |
428 | 10 | 9 |
488 | 10 | 10 |
432 | 10 | 11 |
633 | 10 | 12 |
我已经设法使用以下代码获得给定
Id
的ParentId
s,并且能够递归地对Id
的Id
的顺序进行排序:
DECLARE @myTable TABLE (Id INT, ParentID INT, [Order] INT)
INSERT INTO @myTable
VALUES
(1,1,1),
(2,2,2),
(3,3,3),
(4,1,2),
(5,1,1),
(6,1,3),
(7,4,1),
(8,2,2),
(9,2,1),
(100,3,1),
(10,10,1),
(194,10,1),
(295,194,1),
(205,194,2),
(322,10,2),
(215,194,3),
(404,10,3),
(435,10,4),
(507,10,5),
(206,10,6),
(330,10,7),
(425,10,8),
(428,10,9),
(488,10,10),
(432,10,11),
(633,10,12)
IF OBJECT_ID(N'tempdb..#myTable') IS NOT NULL
BEGIN
DROP TABLE #myTable
END
SELECT *
INTO #myTable
FROM @myTable;
WITH ChildHierarchy AS
(
-- Base query: find the direct children of the given ParentId
SELECT
Id, ParentId, [Order], 0 AS HierarchyLevel,
CAST([Order] AS VARCHAR(MAX)) AS SortPath
FROM
#myTable
WHERE
ParentId = 1 AND Id != 1
UNION ALL
-- Recursive query: find the children of each child
SELECT
O.Id, O.ParentId, O.[Order], HierarchyLevel + 1,
CAST(ChildHierarchy.SortPath + '.' + CAST(O.[Order] AS VARCHAR(MAX)) AS VARCHAR(MAX))
FROM
#myTable O
INNER JOIN
ChildHierarchy ON O.ParentId = ChildHierarchy.Id
)
SELECT *
FROM
(SELECT
Id, ParentId, [Order], CAST(0 AS varchar) AS HierarchyLevel,
CAST(0 AS varchar) AS SortPath
FROM
#myTable
WHERE
Id = 1
UNION
SELECT *
FROM ChildHierarchy) AS X
ORDER BY
[Order] ASC, X.SortPath ASC
除了测试
ParentId
= 10 之外,所有测试都产生写入顺序:
在@myTable
中,每行只会有一个层次结构(即,父母的孩子的孩子不会在另一列的一行中)感谢任何帮助。
如果 SortPath 列的类型无关紧要,可以使用 varbinary 而不是 varchar。这将有助于正确排序。
DECLARE @root INT
SET @root = 10;
WITH ChildHierarchy AS
(
-- Base query: find the direct children of the given ParentId
SELECT
Id, ParentId, [Order], 0 AS HierarchyLevel,
CAST([Order] AS VARBINARY(MAX)) AS SortPath
FROM
#myTable
WHERE
ParentId = @root AND Id != @root
UNION ALL
-- Recursive query: find the children of each child
SELECT
O.Id, O.ParentId, O.[Order], HierarchyLevel + 1,
ChildHierarchy.SortPath + CAST(o.[Order] AS BINARY(2))
FROM
#myTable O
INNER JOIN
ChildHierarchy ON O.ParentId = ChildHierarchy.Id
)
SELECT *
FROM
(SELECT *
FROM ChildHierarchy
UNION
SELECT
Id, ParentId, [Order], CAST(0 AS varchar) AS HierarchyLevel,
CAST(0 AS VARBINARY) AS SortPath
FROM
#myTable
WHERE
Id = @root) AS X
ORDER BY
X.[SortPath] ASC
结果会是这样的:
你可以使用
hierarchyid
。 hierarchyid
就是为这种树形表设计的,允许按层次排序和查询。
您首先进行常规递归 CTE。将每个递归
[Order]
值连接在一起,由 /
分隔。然后简单地将最终结果转换为hierarchyid
并按它排序。
WITH cte AS
(
SELECT
t.Id,
t.ParentId,
t.[Order],
CONCAT('/', CAST([Order] AS varchar(8000)), '/') AS SortPath
FROM #myTable t
WHERE t.Id = 1
UNION ALL
SELECT
t.Id,
t.ParentId,
t.[Order],
CONCAT(cte.SortPath, t.[Order], '/')
FROM #myTable t
INNER JOIN cte ON cte.Id = t.ParentId
AND t.Id <> t.ParentId -- remove root
)
SELECT
Id,
ParentId,
[Order],
SortPathId = CAST(SortPath AS hierarchyid)
FROM cte
ORDER BY
SortPathId;
我必须说,你目前的设计是有问题的。根值本身不应该有
ParentId
,而应该有 NULL
。这将消除对上面注释行的要求。