Reputation: 296
I am trying to achieve the following requirement:
I have a hierarchy of products with their spare parts. Those spare parts can have other parts in a graph that can potentially have several layers of depth.
My goal is to write a T-SQL query that allows me to order by any column of the table (in the example Product, Price or EndOfWarrantyDate) but keeping the hierarchy. In other words, I want to order only the parent items, without worrying about the children that should come into play ONLY to follow their parents.
In the example below, if you notice, the green rows are sorted by "EndOfWarrantyDate", but I still want to show the children and sub-children next to their parent (I don't care about ordering the children, as long as the hierarchy is respected).
NOTE: the Id in the table above is just there to better identify the hierarchy, in the db I have a normal identity column as Id.
And, if possible, I would like to do that in a single SQL query, without having to collect the parent items first, order them and then get the related children and subchildren.
My CTE at the moment looks like this, but I have no idea how to achieve the sorting. Tried with a PARTITION, but don't know how to apply it properly to solve my issue.
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
Any help is very appreciated, thank you!
Upvotes: 1
Views: 229
Reputation: 4042
Preserve the parent EndOfWarrantyDate
as part of your recursive CTE. This way you can sort on it in the end.
Sample data
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');
Solution
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
Result
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
Upvotes: 1