在 SQL Server 2012 中的多级层次结构查询中按特定顺序排列子项
Order child items in a certain order in a multi-level hierarchy query in SQL Server 2012
我在 SQL Server 2012 中有一个名为 Items
的 table,它包含通过 ParentId
列在父子关系中彼此相关的不同项目.此 table 包含 ItemId 为 429965 的最顶层项目及其子级、孙级、曾孙级等,即它包含最顶级项目的多级层次结构。
在 SQL Fiddle of this problem 上有我尝试过的样本数据和查询演示。 Position
的计算列说明层次结构项目的显示顺序。
我正在尝试获取 最顶层 项的完整层次结构,以便满足以下 requirements
条件:
- 在此多级层次结构中,父项紧跟其子项
- 子项需要按
CreateDate
. 的升序显示在此层次结构中
我试过的查询达到了要求 #1,但没有达到要求 #2。
问题
除了使用现有递归查询的要求 #1 之外,如何实现要求 #2?我不能简单地按 PathStr、CreateDate 排序,因为在我的多级层次结构中没有两条路径是相同的。
架构和示例数据创建查询
CREATE TABLE Items (
ItemId int PRIMARY KEY,
ParentId int,
CreateDate datetime
);
INSERT Items
VALUES (44129, 429965, CONVERT(datetime, '2016-01-01 17:30:55.760', 121)),
(61291, 203905, CONVERT(datetime, '2016-01-02 20:18:35.770', 121)),
(157898, 335625, CONVERT(datetime, '2016-01-01 00:00:06.420', 121)),
(191951, 778472, CONVERT(datetime, '2016-01-01 00:00:01.400', 121)),
(203905, 960767, CONVERT(datetime, '2016-01-01 00:00:01.310', 121)),
(265468, 429965, CONVERT(datetime, '2016-05-01 06:07:26.690', 121)),
(268246, 265468, CONVERT(datetime, '2016-10-06 13:41:55.990', 121)),
(283015, 394157, CONVERT(datetime, '2017-12-03 01:58:08.710', 121)),
(299356, 443367, CONVERT(datetime, '2016-01-01 00:00:01.400', 121)),
(335625, 894441, CONVERT(datetime, '2016-11-06 21:27:00.270', 121)),
(338413, 968392, CONVERT(datetime, '2016-11-21 07:15:48.010', 121)),
(394157, 785375, CONVERT(datetime, '2016-05-19 09:19:28.500', 121)),
(397189, 894441, CONVERT(datetime, '2016-01-01 13:34:03.980', 121)),
(404536, 894441, CONVERT(datetime, '2016-01-01 00:00:16.850', 121)),
(429965, 0, CONVERT(datetime, '2016-01-01 00:00:06.090', 121)),
(439536, 968392, CONVERT(datetime, '2017-03-25 23:51:48.570', 121)),
(443367, 191951, CONVERT(datetime, '2016-01-01 00:00:01.090', 121)),
(778472, 394157, CONVERT(datetime, '2016-01-02 20:43:59.760', 121)),
(785375, 910250, CONVERT(datetime, '2017-10-19 03:59:14.950', 121)),
(894441, 265468, CONVERT(datetime, '2016-01-01 00:00:08.600', 121)),
(910250, 268246, CONVERT(datetime, '2016-07-21 00:43:47.420', 121)),
(927248, 785375, CONVERT(datetime, '2017-02-13 04:19:46.340', 121)),
(960767, 335625, CONVERT(datetime, '2016-01-01 00:00:01.960', 121)),
(968392, 785375, CONVERT(datetime, '2017-09-10 02:15:25.780', 121))
我试过的查询
WITH x (ItemId, ParentId, PathStr, CreateDate)
AS (SELECT
ItemId,
0 AS ParentId,
CAST(ItemId AS varchar(max)) AS Pathstr,
CreateDate
FROM Items
WHERE ItemId = 429965
UNION ALL
--get children for each parent ( c is for child table and x is for parent table)
SELECT
i.ItemId,
i.ParentId,
x.PathStr + '-' + CAST(i.ItemId AS varchar(max)),
i.CreateDate
FROM Items i
INNER JOIN x
ON x.ItemId = i.ParentId)
SELECT
*,
ROW_NUMBER() OVER (ORDER BY PathStr) AS Position
FROM x;
在上面的查询中,Position
为 2 和 24 的项目按 CreateDate 的降序出现,并且都是同一父项的子项。如果顺序正确,则位置为 24 的项目实际上应该有一个位置 2,而位置为 2 的项目应该有一个位置 24。
你很接近...
我添加了一个 Step
列,您可以使用它来查看层次结构中的 级别 。此外,我添加了一个 DatePosition 来检查给定 Stel 的所有日期是否按升序排列。好像还行:
WITH x (ItemId, ParentId, PathStr, CreateDate,Step)
AS (SELECT
ItemId,
0 AS ParentId,
CAST(ItemId AS varchar(max)) AS Pathstr,
CreateDate,
1 AS Step
FROM Items
WHERE ItemId = 429965
UNION ALL
--get children for each parent ( c is for child table and x is for parent table)
SELECT
i.ItemId,
i.ParentId,
x.PathStr + '-' + CAST(i.ItemId AS varchar(max)),
i.CreateDate,
x.Step+1
FROM Items i
INNER JOIN x
ON x.ItemId = i.ParentId)
SELECT
*,
ROW_NUMBER() OVER (ORDER BY PathStr) AS Position,
ROW_NUMBER() OVER (ORDER BY CreateDate) AS DatePosition
FROM x
ORDER BY Step,DatePosition;
更新:线程讨论...
试试这个
WITH x (ItemId, ParentId, PathStr,CreateDate,Step)
AS (SELECT ItemId,
0 AS ParentId,
CAST(REPLACE(STR(ItemId,9),' ','0') AS VARCHAR(MAX)) AS PathStr,
CreateDate,
1 AS Step
FROM Items
WHERE ItemId = 429965
UNION ALL
--get children for each parent ( i is for child table and x is for parent table)
SELECT i.ItemId,
i.ParentId,
CAST(x.PathStr + '-' + REPLACE(STR(i.SortNmbr,3),' ','0') AS VARCHAR(MAX)) + '-' + CAST(REPLACE(STR(i.ItemId,9),' ','0') AS VARCHAR(MAX)),
i.CreateDate,
x.Step+1
FROM x
CROSS APPLY(SELECT *, ROW_NUMBER() OVER(ORDER BY CreateDate) AS SortNmbr FROM Items WHERE Items.ParentId=x.ItemId) AS i
)
SELECT *
FROM x
ORDER BY PathStr;
如果您想订购递归 cte 的结果,您必须将所需的一切包含到您的 PathStr
中。此外,此排序是按字母数字顺序进行的,因此您必须向所有数字添加零,直到宽度相等。
我使用 CROSS APPLY
(而不是 INNER JOIN
)对相关行进行排序,并将派生的 SortNmbr
(也填充!)包含到排序字符串中。或者,可以包含日期本身,但这会导致嵌套更深的字符串变得相当大。
我的填充允许 ItemId 的 9
位数字和每个 parent 的答案计数的 3 位数字,这很可能。相应地减少它以减少排序字符串的大小。你可以去掉连字符,它们只是为了更好地阅读。
我在 SQL Server 2012 中有一个名为 Items
的 table,它包含通过 ParentId
列在父子关系中彼此相关的不同项目.此 table 包含 ItemId 为 429965 的最顶层项目及其子级、孙级、曾孙级等,即它包含最顶级项目的多级层次结构。
在 SQL Fiddle of this problem 上有我尝试过的样本数据和查询演示。 Position
的计算列说明层次结构项目的显示顺序。
我正在尝试获取 最顶层 项的完整层次结构,以便满足以下 requirements
条件:
- 在此多级层次结构中,父项紧跟其子项
- 子项需要按
CreateDate
. 的升序显示在此层次结构中
我试过的查询达到了要求 #1,但没有达到要求 #2。
问题
除了使用现有递归查询的要求 #1 之外,如何实现要求 #2?我不能简单地按 PathStr、CreateDate 排序,因为在我的多级层次结构中没有两条路径是相同的。
架构和示例数据创建查询
CREATE TABLE Items (
ItemId int PRIMARY KEY,
ParentId int,
CreateDate datetime
);
INSERT Items
VALUES (44129, 429965, CONVERT(datetime, '2016-01-01 17:30:55.760', 121)),
(61291, 203905, CONVERT(datetime, '2016-01-02 20:18:35.770', 121)),
(157898, 335625, CONVERT(datetime, '2016-01-01 00:00:06.420', 121)),
(191951, 778472, CONVERT(datetime, '2016-01-01 00:00:01.400', 121)),
(203905, 960767, CONVERT(datetime, '2016-01-01 00:00:01.310', 121)),
(265468, 429965, CONVERT(datetime, '2016-05-01 06:07:26.690', 121)),
(268246, 265468, CONVERT(datetime, '2016-10-06 13:41:55.990', 121)),
(283015, 394157, CONVERT(datetime, '2017-12-03 01:58:08.710', 121)),
(299356, 443367, CONVERT(datetime, '2016-01-01 00:00:01.400', 121)),
(335625, 894441, CONVERT(datetime, '2016-11-06 21:27:00.270', 121)),
(338413, 968392, CONVERT(datetime, '2016-11-21 07:15:48.010', 121)),
(394157, 785375, CONVERT(datetime, '2016-05-19 09:19:28.500', 121)),
(397189, 894441, CONVERT(datetime, '2016-01-01 13:34:03.980', 121)),
(404536, 894441, CONVERT(datetime, '2016-01-01 00:00:16.850', 121)),
(429965, 0, CONVERT(datetime, '2016-01-01 00:00:06.090', 121)),
(439536, 968392, CONVERT(datetime, '2017-03-25 23:51:48.570', 121)),
(443367, 191951, CONVERT(datetime, '2016-01-01 00:00:01.090', 121)),
(778472, 394157, CONVERT(datetime, '2016-01-02 20:43:59.760', 121)),
(785375, 910250, CONVERT(datetime, '2017-10-19 03:59:14.950', 121)),
(894441, 265468, CONVERT(datetime, '2016-01-01 00:00:08.600', 121)),
(910250, 268246, CONVERT(datetime, '2016-07-21 00:43:47.420', 121)),
(927248, 785375, CONVERT(datetime, '2017-02-13 04:19:46.340', 121)),
(960767, 335625, CONVERT(datetime, '2016-01-01 00:00:01.960', 121)),
(968392, 785375, CONVERT(datetime, '2017-09-10 02:15:25.780', 121))
我试过的查询
WITH x (ItemId, ParentId, PathStr, CreateDate)
AS (SELECT
ItemId,
0 AS ParentId,
CAST(ItemId AS varchar(max)) AS Pathstr,
CreateDate
FROM Items
WHERE ItemId = 429965
UNION ALL
--get children for each parent ( c is for child table and x is for parent table)
SELECT
i.ItemId,
i.ParentId,
x.PathStr + '-' + CAST(i.ItemId AS varchar(max)),
i.CreateDate
FROM Items i
INNER JOIN x
ON x.ItemId = i.ParentId)
SELECT
*,
ROW_NUMBER() OVER (ORDER BY PathStr) AS Position
FROM x;
在上面的查询中,Position
为 2 和 24 的项目按 CreateDate 的降序出现,并且都是同一父项的子项。如果顺序正确,则位置为 24 的项目实际上应该有一个位置 2,而位置为 2 的项目应该有一个位置 24。
你很接近...
我添加了一个 Step
列,您可以使用它来查看层次结构中的 级别 。此外,我添加了一个 DatePosition 来检查给定 Stel 的所有日期是否按升序排列。好像还行:
WITH x (ItemId, ParentId, PathStr, CreateDate,Step)
AS (SELECT
ItemId,
0 AS ParentId,
CAST(ItemId AS varchar(max)) AS Pathstr,
CreateDate,
1 AS Step
FROM Items
WHERE ItemId = 429965
UNION ALL
--get children for each parent ( c is for child table and x is for parent table)
SELECT
i.ItemId,
i.ParentId,
x.PathStr + '-' + CAST(i.ItemId AS varchar(max)),
i.CreateDate,
x.Step+1
FROM Items i
INNER JOIN x
ON x.ItemId = i.ParentId)
SELECT
*,
ROW_NUMBER() OVER (ORDER BY PathStr) AS Position,
ROW_NUMBER() OVER (ORDER BY CreateDate) AS DatePosition
FROM x
ORDER BY Step,DatePosition;
更新:线程讨论...
试试这个
WITH x (ItemId, ParentId, PathStr,CreateDate,Step)
AS (SELECT ItemId,
0 AS ParentId,
CAST(REPLACE(STR(ItemId,9),' ','0') AS VARCHAR(MAX)) AS PathStr,
CreateDate,
1 AS Step
FROM Items
WHERE ItemId = 429965
UNION ALL
--get children for each parent ( i is for child table and x is for parent table)
SELECT i.ItemId,
i.ParentId,
CAST(x.PathStr + '-' + REPLACE(STR(i.SortNmbr,3),' ','0') AS VARCHAR(MAX)) + '-' + CAST(REPLACE(STR(i.ItemId,9),' ','0') AS VARCHAR(MAX)),
i.CreateDate,
x.Step+1
FROM x
CROSS APPLY(SELECT *, ROW_NUMBER() OVER(ORDER BY CreateDate) AS SortNmbr FROM Items WHERE Items.ParentId=x.ItemId) AS i
)
SELECT *
FROM x
ORDER BY PathStr;
如果您想订购递归 cte 的结果,您必须将所需的一切包含到您的 PathStr
中。此外,此排序是按字母数字顺序进行的,因此您必须向所有数字添加零,直到宽度相等。
我使用 CROSS APPLY
(而不是 INNER JOIN
)对相关行进行排序,并将派生的 SortNmbr
(也填充!)包含到排序字符串中。或者,可以包含日期本身,但这会导致嵌套更深的字符串变得相当大。
我的填充允许 ItemId 的 9
位数字和每个 parent 的答案计数的 3 位数字,这很可能。相应地减少它以减少排序字符串的大小。你可以去掉连字符,它们只是为了更好地阅读。