【问题标题】:T-SQL recursive query that sorts by parent level only仅按父级排序的 T-SQL 递归查询
【发布时间】:2020-11-28 13:43:45
【问题描述】:

我正在努力实现以下要求:

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

我的目标是编写一个 T-SQL 查询,允许我按表的任何列(在示例产品、价格或 EndOfWarrantyDate 中)进行排序,但保持层次结构。换句话说,我只想订购父母的物品,而不用担心孩子应该只跟随父母来玩耍

在下面的示例中,如果您注意到,绿色行按“EndOfWarrantyDate”排序,但我仍然想在其父项旁边显示子项和子项(我不关心对子项进行排序,如只要尊重层次结构)。

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

而且,如果可能的话,我想在单个 SQL 查询中执行此操作,而不必先收集父项、对其进行排序然后获取相关的子项和子项。

我现在的 CTE 看起来像这样,但我不知道如何实现排序。尝试使用 PARTITION,但不知道如何正确应用它来解决我的问题。

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

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

【问题讨论】:

  • 您可以使用order by case when @SortColumn = 'Product' then Product when ... end 在递归CTE 的anchor 中添加Row_Number()。请注意,所有列都需要转换为兼容类型,例如保修日期需要是一个可排序的字符串:YYYYMMDD。在 CTE 的 recursive 部分中,只需将值传播给孩子。

标签: sql-server tsql common-table-expression


【解决方案1】:

保留父 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

【讨论】:

  • 这并不能完全涵盖问题的“按任何列排序”部分,而无需将所有有趣的列添加到 CTE 为 Parent<Something> 并将 order by 子句更改为 @987654327 @ 表达式选择适当的Parent<Something> 列。
  • 感谢 Sander,对您的解决方案稍作修改似乎可以按预期工作。基本上我的 Id 列实际上只是一个整数 Id,所以我创建了一个计算列来实现您在我的表中看到的层次结构 Id。为了回答@HABO,该解决方案适用于任何列,因为我将通过 C# 中的 Dapper 调用它,并且我可以参数化 SortingColumn(我不关心计算字段的名称,它可以是固定的“SortingField ' 列)
猜你喜欢
  • 2015-09-07
  • 1970-01-01
  • 2013-09-15
  • 2013-01-31
  • 2015-01-09
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
  • 2014-06-02
相关资源
最近更新 更多