父级子层次结构,按名称排序

时间:2017-12-15 10:55:31

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

我想为以下内容创建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查询。

P.S。只有一个层次结构。

5 个答案:

答案 0 :(得分:2)

SQL Server中几乎每个层次结构问题都是通过递归cte解决的。 你没有提到是否可以有多个级别的层次结构,但由于没有办法阻止那个其他的,而是编写一个而不是触发器或基于udf的检查约束,我假设可以有多个层级行。
这个答案的诀窍是按字母顺序排序数字与数字排序不同的事实 如果您将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

答案 1 :(得分:1)

试试这个

;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

答案 2 :(得分: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子句中,以解释它是如何工作的。

答案 3 :(得分:1)

如果您不了解孩子的深度,我通常会用动态Sql来解决这个问题。但是因为你似乎只有一个深度级别为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

答案 4 :(得分:0)

在进行一些调试之后,我发现,如果其中一个层次结构级别具有> 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个字符。但是,如果您需要更多,只需自己添加即可,不要忘记根据层次结构中的级别数乘以所需字符的长度来更改“排序”列的长度。