T-SQL 仅按 parent 级别排序的递归查询

T-SQL recursive query that sorts by parent level only

我正在尝试实现以下要求:

我有一个产品及其备件的层次结构。这些备件可以在图表中包含其他部分,这些部分可能具有几层深度。

我的目标是编写一个 T-SQL 查询,允许我按 table 的任何列(在示例产品、价格或 EndOfWarrantyDate 中)进行排序,但保留层次结构。换句话说,我只想订购 parent 项,而不用担心应该只在 parent 之后发挥作用的 children .

在下面的示例中,如果您注意到,绿色行按“EndOfWarrantyDate”排序,但我仍想在 parent 旁边显示 children 和 sub-children (我不关心订购 children,只要尊重层次结构即可)。

注意:上面table中的Id只是为了更好地识别层次结构,在数据库中我有一个普通的标识列作为Id。

而且,如果可能的话,我想在单个 SQL 查询中执行此操作,而不必先收集 parent 项,订购它们,然后获取相关的 children 和子children.

目前我的CTE是这样的,但是我不知道如何实现排序。尝试使用分区,但不知道如何正确应用它来解决我的问题。

WITH
  cteProducts (Id, Product, ParentProductId, Price, EndOfWarrantyDate, Depth, RootId)
  AS
  (
-->>>>>>>>>>Root>>>>>>>>>>>>>>>>>
    SELECT Id, Product, ParentProductId, Price, EndOfWarrantyDate, 0 as Depth, Id as RootId
    FROM Products
    WHERE ParentProductId IS NULL
-->>>>>>>>>>Root>>>>>>>>>>>>>>>>>
    UNION ALL
-->>>>>>>>>>Children>>>>>>>>>>>>>>>>>    
    SELECT e.Id, e.Product, e.ParentProductId, e.Price, e.EndOfWarrantyDate, r.Depth + 1, r.RootId
    FROM Products e
      INNER JOIN cteProducts r
        ON e.ParentProductId = r.Id
-->>>>>>>>>>Children>>>>>>>>>>>>>>>>>
  )
SELECT
  Id, Product, ParentProductId, Price, EndOfWarrantyDate, Depth, RootId
FROM cteProducts
--ORDER BY RootId, EndOfWarrantyDate

非常感谢任何帮助,谢谢!

保留父 EndOfWarrantyDate 作为递归 CTE 的一部分。这样最后就可以排序了

示例数据

declare @Products table
(
    Id nvarchar(10),
    Product nvarchar(20),
    ParentProductId nvarchar(10),
    Price int,
    EndOfWarrantyDate date
);

insert into @Products (Id, Product, ParentProductId, Price, EndOfWarrantyDate) values
('1',       'Vacuum',           null,   130,    '2013-04-02'),
('1.1',     'Power Unit',       '1',    200,    '2024-01-01'),
('1.1.1',   'Unit part',        '1.1',  100,    '2024-01-01'),
('1.2',     'Filter',           '1',    10,     '2022-07-15'),
('2',       'Laptop',           null,   600,    '2023-06-01'),
('2.1',     'Hard Disk',        '2',    200,    '2024-03-01'),
('3',       'Washing Machine',  null,   1000,   '2023-12-01');

解决方案

WITH
  cteProducts (Id, Product, ParentProductId, Price, EndOfWarrantyDate, Depth, RootId, ParentDate)
  AS
  (
-->>>>>>>>>>Root>>>>>>>>>>>>>>>>>
    SELECT Id, Product, ParentProductId, Price, EndOfWarrantyDate, 0 as 'Depth', Id as 'RootId'
            ,EndOfWarrantyDate as 'ParentDate' -- new field
    FROM @Products
    WHERE ParentProductId IS NULL
-->>>>>>>>>>Root>>>>>>>>>>>>>>>>>
    UNION ALL
-->>>>>>>>>>Children>>>>>>>>>>>>>>>>>    
    SELECT e.Id, e.Product, e.ParentProductId, e.Price, e.EndOfWarrantyDate, r.Depth + 1, r.RootId
            ,r.ParentDate -- new field
    FROM @Products e
      INNER JOIN cteProducts r
        ON e.ParentProductId = r.Id
-->>>>>>>>>>Children>>>>>>>>>>>>>>>>>
  )
SELECT ParentDate,
  Id, Product, ParentProductId, Price, EndOfWarrantyDate, Depth, RootId
FROM cteProducts
order by ParentDate, Id; -- new field as first sort field

结果

ParentDate Id         Product              ParentProductId Price       EndOfWarrantyDate Depth       RootId
---------- ---------- -------------------- --------------- ----------- ----------------- ----------- ----------
2013-04-02 1          Vacuum               NULL            130         2013-04-02        0           1
2013-04-02 1.1        Power Unit           1               200         2024-01-01        1           1
2013-04-02 1.1.1      Unit part            1.1             100         2024-01-01        2           1
2013-04-02 1.2        Filter               1               10          2022-07-15        1           1
2023-06-01 2          Laptop               NULL            600         2023-06-01        0           2
2023-06-01 2.1        Hard Disk            2               200         2024-03-01        1           2
2023-12-01 3          Washing Machine      NULL            1000        2023-12-01        0           3