The following queries return the exact result set that you requested. All this works by calculating the full path to the root nodes and using some technique to arrange this path.
SQL Server 2008 and later. Here, by converting to a hierarchyid data type, SQL Server handles the order correctly.
WITH Data AS ( SELECT ID, Name, ParentID, Depth = 0, Ancestry = '/' + Convert(varchar(max), ID) + '/' FROM hierarchy WHERE ParentID IS NULL UNION ALL SELECT H.ID, H.Name, H.ParentID, D.Depth + 1, Ancestry = D.Ancestry + Convert(varchar(max), H.ID) + '/' FROM Data D INNER JOIN hierarchy H ON H.ParentID = D.ID ) SELECT ID, Name, ParentID, Depth FROM Data ORDER BY Convert(hierarchyid, Ancestry);
SQL Server 2005 and later. We can convert the ID values ββto a string and lay them out so that they are sorted.
WITH Data AS ( SELECT ID, Name, ParentID, Depth = 0, Ancestry = Right('0000000000' + Convert(varchar(max), ID), 10) FROM hierarchy WHERE ParentID IS NULL UNION ALL SELECT H.ID, H.Name, H.ParentID, Depth + 1, Ancestry = D.Ancestry + Right('0000000000' + Convert(varchar(max), H.ID), 10) FROM Data D INNER JOIN hierarchy H ON H.ParentID = D.ID ) SELECT ID, Name, ParentID, Depth FROM Data ORDER BY Ancestry;
We can also use varbinary (otherwise it is the same as the previous request):
WITH Data AS ( SELECT ID, Name, ParentID, Depth = 0, Ancestry = Convert(varbinary(max), Convert(varbinary(4), ID)) FROM hierarchy WHERE ParentID IS NULL UNION ALL SELECT H.ID, H.Name, H.ParentID, Depth + 1, Ancestry = D.Ancestry + Convert(varbinary(4), H.ID) FROM Data D INNER JOIN hierarchy H ON H.ParentID = D.ID ) SELECT ID, Name, ParentID, Depth FROM Data ORDER BY Ancestry;
SQL Server 2000 and above, allowing the tree to have a maximum of 800 levels:
SELECT *, Ancestry = CASE WHEN ParentID IS NULL THEN Convert(varchar(8000), Right('0000000000' + Convert(varchar(10), ID), 10)) ELSE '' END, Depth = 0 INTO
The same varbinary conversion can be performed, allowing deeper up to 2000 levels.