Parent child 按名称排序的层次结构
Parent child hierarchy with order by on name
我想为以下内容创建 SQL 服务器查询。我有这样的数据:
Id Name parentId
1 STU 0
2 XYZ 5
3 PQR 5
4 EFG 0
5 ABC 0
6 HIJ 1
7 DEF 1
这是我正在使用的查询:
SELECT *
FROM TABLE
ORDER BY CASE WHEN parentId = 0 THEN id ELSE parentId END ASC, id ASC
输出(parent和它的children一起排序):
Id Name parentId
1 STU 0
6 HIJ 1
7 DEF 1
4 EFG 0
5 ABC 0
2 XYZ 5
3 PQR 5
现在我也需要按姓名排序,首先按 parent 的姓名,然后是所有 children,也按姓名排序。预期输出为:
Id Name parentId
5 ABC 0
3 PQR 5
2 XYZ 5
4 EFG 0
1 STU 0
7 DEF 1
6 HIJ 1
有人对此有解决方案吗?为此,我需要严格的 SQL 服务器查询。
P.S。将只有一层层次结构。
试试这个
;WITH CTE
AS
(
SELECT
RN =0,
ID,
NAME,
parentId = ID
FROM T1
WHERE parentId = 0
UNION ALL
SELECT
RN = ROW_NUMBER() OVER(PARTITION BY T1.parentId ORDER BY T1.name asc),
T1.ID,
T1.NAME,
T1.parentId
FROM T1
INNER JOIN T1 T2
ON T1.parentId =T2.ID
)
SELECT
id,
name,
parentid
FROM CTE
ORDER BY parentId DESC,RN
几乎 SQL 服务器中的每个层次结构问题都用递归 cte 解决了。
您没有提到是否可以有多个层次结构,但是由于没有办法阻止其他层次结构然后编写一个代替触发器或基于 udf 的检查约束,我继续假设可以有多个层次结构-级层级行。
这个答案的诀窍在于,按字母顺序对数字进行排序与按数字对数字进行排序是不同的。
如果您将 1, 11, 2, 13, 21, 3
排序为 数字 排序,
你会得到 1, 2, 3, 11, 13, 21
.
但是,按 alphabetical 排序相同的数字,
你会得到 1, 11, 13, 2, 21, 3
。
废话不多说了,让我们看看代码吧!
首先,创建并填充示例 table(请 在您以后的问题中为我们省去这一步):
DECLARE @T AS TABLE
(
Id int,
[Name] char(3),
parentId int
)
INSERT INTO @T (Id, [Name], parentId) VALUES
(1, 'STU', 0),
(2, 'XYZ', 5),
(3, 'PQR', 5),
(4, 'EFG', 0),
(5, 'ABC', 0),
(6, 'HIJ', 1),
(7, 'DEF', 1),
(8, 'AAA', 3),
(9, 'ZZZ', 3)
注意:我为孙辈添加了两行以检查多级层次结构。
cte:
;WITH CTE AS
(
SELECT Id,
[Name],
ParentId,
-- Row_Number returns a bigint - max value have 19 digits
CAST(ROW_NUMBER() OVER(ORDER BY [Name]) as varchar(19)) As Sort
FROM @T
WHERE parentId = 0
UNION ALL
SELECT T.Id,
T.[Name],
T.ParentId,
CAST(Sort + CAST(ROW_NUMBER() OVER(ORDER BY T.[Name]) as varchar(19)) as varchar(19))
FROM @T T
JOIN CTE ON T.parentId = CTE.Id
)
查询:
SELECT Id, [Name], ParentId
FROM CTE
ORDER BY Sort -- alphabetic sort will order 11 before 2...
结果:
Id Name ParentId
5 ABC 0
3 PQR 5
8 AAA 3
9 ZZZ 3
2 XYZ 5
4 EFG 0
1 STU 0
7 DEF 1
6 HIJ 1
它相当简单,真的:
SELECT
category.*,
-- bring parent and its children together
CASE WHEN parent.Id IS NULL THEN category.Name ELSE parent.Name END AS sort1,
-- move parent to top followed by its children
CASE WHEN parent.Id IS NULL THEN NULL ELSE category.Name END AS sort2
FROM category
LEFT JOIN category AS parent ON category.parentId = parent.Id
ORDER BY sort1, sort2
输出:
+------+------+----------+-------+-------+
| Id | Name | parentId | sort1 | sort2 |
+------+------+----------+-------+-------+
| 5 | ABC | 0 | ABC | NULL |
| 3 | PQR | 5 | ABC | PQR |
| 2 | XYZ | 5 | ABC | XYZ |
| 4 | EFG | 0 | EFG | NULL |
| 1 | STU | 0 | STU | NULL |
| 7 | DEF | 1 | STU | DEF |
| 6 | HIJ | 1 | STU | HIJ |
+------+------+----------+-------+-------+
请注意,我已将排序计算放在 SELECT
子句中以解释其工作原理。
如果您不知道子深度,我通常使用动态 Sql 来解决这个问题。但是因为你的 depthLevel 似乎只有 2,一个简单的版本可能会起作用:
declare @tempT table(ID int, name varchar(3), parentID int, sortLevel1 int, sortlevel2 int)
insert into @tempT
select t1.ID,t1.name,t1.parentID,RowNumber() Over(order by (select null)),-1
from table t1
where parentId=0
order by t1.name
insert into @tempT
select t2.ID,t2.name,t2.parentID,t1.sortLevel1,RowNumber() Over(order by (select null))
from table t1
join @tempT t2 on t1.id=t2.parentID
order by t2.name
select * from @temp order by t1.sortLevel1, sortLevel2
经过一些调试后,我发现如果其中一个层级有 >9 个条目,Zohar Peled
的解决方案就会失败。我通过修改他的代码解决了这个问题,他在代码中添加了前导零来构建“排序”列。
;WITH CTE AS
(
SELECT Id,
[Name],
ParentId,
-- Row_Number returns a bigint - max value have 19 digits
CAST(FORMAT(ROW_NUMBER() OVER(ORDER BY [Name]), 'D4') as varchar(12)) As Sort
FROM @T
WHERE parentId = 0
UNION ALL
SELECT T.Id,
T.[Name],
T.ParentId,
CAST(Sort + CAST(FORMAT(ROW_NUMBER() OVER(ORDER BY [Name]), 'D4') as varchar(4)) as varchar(12))
FROM @T T
JOIN CTE ON T.parentId = CTE.Id
)
在我的例子中,我不需要超过 4 个字符。但是,如果您需要更多,只需自己添加,并且不要忘记根据您在层次结构中的级别数乘以您需要的字符长度来更改排序列的长度。
我想为以下内容创建 SQL 服务器查询。我有这样的数据:
Id Name parentId
1 STU 0
2 XYZ 5
3 PQR 5
4 EFG 0
5 ABC 0
6 HIJ 1
7 DEF 1
这是我正在使用的查询:
SELECT *
FROM TABLE
ORDER BY CASE WHEN parentId = 0 THEN id ELSE parentId END ASC, id ASC
输出(parent和它的children一起排序):
Id Name parentId
1 STU 0
6 HIJ 1
7 DEF 1
4 EFG 0
5 ABC 0
2 XYZ 5
3 PQR 5
现在我也需要按姓名排序,首先按 parent 的姓名,然后是所有 children,也按姓名排序。预期输出为:
Id Name parentId
5 ABC 0
3 PQR 5
2 XYZ 5
4 EFG 0
1 STU 0
7 DEF 1
6 HIJ 1
有人对此有解决方案吗?为此,我需要严格的 SQL 服务器查询。
P.S。将只有一层层次结构。
试试这个
;WITH CTE
AS
(
SELECT
RN =0,
ID,
NAME,
parentId = ID
FROM T1
WHERE parentId = 0
UNION ALL
SELECT
RN = ROW_NUMBER() OVER(PARTITION BY T1.parentId ORDER BY T1.name asc),
T1.ID,
T1.NAME,
T1.parentId
FROM T1
INNER JOIN T1 T2
ON T1.parentId =T2.ID
)
SELECT
id,
name,
parentid
FROM CTE
ORDER BY parentId DESC,RN
几乎 SQL 服务器中的每个层次结构问题都用递归 cte 解决了。
您没有提到是否可以有多个层次结构,但是由于没有办法阻止其他层次结构然后编写一个代替触发器或基于 udf 的检查约束,我继续假设可以有多个层次结构-级层级行。
这个答案的诀窍在于,按字母顺序对数字进行排序与按数字对数字进行排序是不同的。
如果您将 1, 11, 2, 13, 21, 3
排序为 数字 排序,
你会得到 1, 2, 3, 11, 13, 21
.
但是,按 alphabetical 排序相同的数字,
你会得到 1, 11, 13, 2, 21, 3
。
废话不多说了,让我们看看代码吧!
首先,创建并填充示例 table(请 在您以后的问题中为我们省去这一步):
DECLARE @T AS TABLE
(
Id int,
[Name] char(3),
parentId int
)
INSERT INTO @T (Id, [Name], parentId) VALUES
(1, 'STU', 0),
(2, 'XYZ', 5),
(3, 'PQR', 5),
(4, 'EFG', 0),
(5, 'ABC', 0),
(6, 'HIJ', 1),
(7, 'DEF', 1),
(8, 'AAA', 3),
(9, 'ZZZ', 3)
注意:我为孙辈添加了两行以检查多级层次结构。
cte:
;WITH CTE AS
(
SELECT Id,
[Name],
ParentId,
-- Row_Number returns a bigint - max value have 19 digits
CAST(ROW_NUMBER() OVER(ORDER BY [Name]) as varchar(19)) As Sort
FROM @T
WHERE parentId = 0
UNION ALL
SELECT T.Id,
T.[Name],
T.ParentId,
CAST(Sort + CAST(ROW_NUMBER() OVER(ORDER BY T.[Name]) as varchar(19)) as varchar(19))
FROM @T T
JOIN CTE ON T.parentId = CTE.Id
)
查询:
SELECT Id, [Name], ParentId
FROM CTE
ORDER BY Sort -- alphabetic sort will order 11 before 2...
结果:
Id Name ParentId
5 ABC 0
3 PQR 5
8 AAA 3
9 ZZZ 3
2 XYZ 5
4 EFG 0
1 STU 0
7 DEF 1
6 HIJ 1
它相当简单,真的:
SELECT
category.*,
-- bring parent and its children together
CASE WHEN parent.Id IS NULL THEN category.Name ELSE parent.Name END AS sort1,
-- move parent to top followed by its children
CASE WHEN parent.Id IS NULL THEN NULL ELSE category.Name END AS sort2
FROM category
LEFT JOIN category AS parent ON category.parentId = parent.Id
ORDER BY sort1, sort2
输出:
+------+------+----------+-------+-------+
| Id | Name | parentId | sort1 | sort2 |
+------+------+----------+-------+-------+
| 5 | ABC | 0 | ABC | NULL |
| 3 | PQR | 5 | ABC | PQR |
| 2 | XYZ | 5 | ABC | XYZ |
| 4 | EFG | 0 | EFG | NULL |
| 1 | STU | 0 | STU | NULL |
| 7 | DEF | 1 | STU | DEF |
| 6 | HIJ | 1 | STU | HIJ |
+------+------+----------+-------+-------+
请注意,我已将排序计算放在 SELECT
子句中以解释其工作原理。
如果您不知道子深度,我通常使用动态 Sql 来解决这个问题。但是因为你的 depthLevel 似乎只有 2,一个简单的版本可能会起作用:
declare @tempT table(ID int, name varchar(3), parentID int, sortLevel1 int, sortlevel2 int)
insert into @tempT
select t1.ID,t1.name,t1.parentID,RowNumber() Over(order by (select null)),-1
from table t1
where parentId=0
order by t1.name
insert into @tempT
select t2.ID,t2.name,t2.parentID,t1.sortLevel1,RowNumber() Over(order by (select null))
from table t1
join @tempT t2 on t1.id=t2.parentID
order by t2.name
select * from @temp order by t1.sortLevel1, sortLevel2
经过一些调试后,我发现如果其中一个层级有 >9 个条目,Zohar Peled
的解决方案就会失败。我通过修改他的代码解决了这个问题,他在代码中添加了前导零来构建“排序”列。
;WITH CTE AS
(
SELECT Id,
[Name],
ParentId,
-- Row_Number returns a bigint - max value have 19 digits
CAST(FORMAT(ROW_NUMBER() OVER(ORDER BY [Name]), 'D4') as varchar(12)) As Sort
FROM @T
WHERE parentId = 0
UNION ALL
SELECT T.Id,
T.[Name],
T.ParentId,
CAST(Sort + CAST(FORMAT(ROW_NUMBER() OVER(ORDER BY [Name]), 'D4') as varchar(4)) as varchar(12))
FROM @T T
JOIN CTE ON T.parentId = CTE.Id
)
在我的例子中,我不需要超过 4 个字符。但是,如果您需要更多,只需自己添加,并且不要忘记根据您在层次结构中的级别数乘以您需要的字符长度来更改排序列的长度。