【问题标题】:Parent child hierarchy with order by on name按名称排序的父子层次结构
【发布时间】:2017-12-15 10:55:30
【问题描述】:

我想为以下内容创建 SQL Server 查询。我有这样的数据:

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

输出(父级及其子级一起排序):

Id       Name       parentId
1         STU        0
6         HIJ        1
7         DEF        1
4         EFG        0
5         ABC        0
2         XYZ        5
3         PQR        5

现在我也需要按姓名排序,首先按父母的姓名,然后是所有孩子,也按姓名排序。预期的输出是:

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 Server 查询。

附注将只有一个层次结构。

【问题讨论】:

  • 孩子也能有孩子吗?如果是这样,你也需要迎合吗?
  • 如果你有多个级别怎么办?结果应该先按层次排序再按名称排序?

标签: sql sql-server sql-order-by parent-child


【解决方案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 子句中以解释其工作原理。

【讨论】:

  • 这将因多级层次结构而失败。
  • 它将只有一个层次结构。
  • 谢谢萨尔曼。我将使用这个解决方案,因为它不需要高级功能 + 要求是 1 级父子层次结构,所以它是目前对我来说最好的解决方案。
【解决方案2】:

SQL Server 中几乎每个层次结构问题都通过递归 cte 解决。 您没有提到是否可以有多个层次结构,但是由于没有办法阻止其他,然后编写一个而不是触发器或基于 udf 的检查约束,我假设可以有多个-level 层次结构行。
这个答案的诀窍在于,按字母顺序排序数字与按数字排序不同。
如果您以数字排序对1, 11, 2, 13, 21, 3进行排序,
你会得到1, 2, 3, 11, 13, 21
但是,在字母顺序排序中对相同的数字进行排序,
你会得到1, 11, 13, 2, 21, 3

现在,说够了,让我们看看一些代码!
首先,创建并填充示例表(在您以后的问题中保存我们这一步):

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

【讨论】:

  • 很高兴为您提供帮助 ;-)
【解决方案3】:

试试这个

;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

【讨论】:

  • Row_number() 返回bigint,因此您需要在cte 的锚部分上转换为bigint。此外,这会为顶级行返回错误的父 id,如果有多个层次结构,它将失败。
  • 我已经对其进行了测试并且可以正常工作而无需强制转换为 bigint 并按预期返回行。但我同意这不适用于 1 级以上的记录
  • 可能是版本问题。我在 2016 年测试过,但出现错误。
  • 我用的是 2014。
  • 谢谢你Jayasurya
【解决方案4】:

如果你不知道孩子的深度,我通常用动态 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

【讨论】:

    【解决方案5】:

    经过一些调试,我发现Zohar Peled 的解决方案如果其中一个层次结构级别具有 >9 个条目,则会失败。我通过修改他的代码解决了这个问题,他通过添加前导零来构造一个“排序”列。

    ;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 个字符。但如果您需要更多,请自行添加,不要忘记根据层次结构中的级别数乘以您需要的字符长度来更改排序列的长度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-21
      • 1970-01-01
      • 1970-01-01
      • 2011-04-23
      • 1970-01-01
      • 1970-01-01
      • 2020-05-29
      • 1970-01-01
      相关资源
      最近更新 更多