Gleb
Gleb

Reputation: 1432

How to order rows by hierarchy

I have table with hierarchical, parent-child relations and want to order it by that hierarchy. Table is:

id|parent|type
--------------
1 |0     |1
2 |0     |1
3 |0     |1
4 |0     |2
5 |0     |2
6 |2     |2
7 |3     |2

And as result I want this:

id|parent|type
--------------
1 |0     |1
2 |0     |1
6 |2     |2
3 |0     |1
7 |3     |2
4 |0     |2
5 |0     |2

So I want get something like a tree view where type 1 ordered first and type 2 at the end.

Now I'm trying to use recursion but the order is wrong:

with cte as
(
  select id, parent, type from tbl where id=1
  union all
  select id, parent, type,
  ROW_NUMBER()over(
   order by
         (case when t.type = 1 then 1
            when t.type = 2 then 2
    else 1000
    end) as rn
  from tbl t
  inner join cte c on c.id=t.parent
)
select * from cte
order by rn

How can I do this?

Upvotes: 6

Views: 7569

Answers (3)

Eric
Eric

Reputation: 5743

Using the order by hierarchyid with cte is simple, not test for recursive relations

DECLARE @Data table (Id int identity(1,1) primary key, Parent int, Type int)

INSERT @Data VALUES 
(0, 1),
(0, 1),
(0, 1),
(0, 2),
(0, 2),
(2, 2),
(3, 2)

SELECT * FROM @Data

;WITH level AS
(
    -- The root, build the hierarchy by /{Type}.{Id}/, where Type is important then Id
    SELECT *, -- 0 AS Level,
        '/' + CONVERT(varchar(max), Type + 0.1 * Id) + '/' AS Ordering 
    FROM @Data 
    WHERE Parent = 0
    UNION ALL
    -- Connect the parent with appending the hierarchy
    SELECT d.*, -- c.Level + 1, 
        c.Ordering + CONVERT(varchar(max), d.Type + 0.1 * d.Id) + '/' 
    FROM @Data d INNER JOIN level c ON d.Parent = c.Id
)
SELECT Id, Parent, Type FROM level 
ORDER BY CAST(Ordering as hierarchyid) -- The key part to convert data type

SQL Fiddle

Upvotes: 3

daniel
daniel

Reputation: 1070

with cte as
(
  select *, 1 level, row_number() over (order by id) rn
  from tbl
  where parent = 0
  union all
  select t.*, cte.level + 1 level, cte.rn
  from cte
  inner join tbl t on cte.id = t.parent
)
select id, parent, type from cte order by rn, level

Upvotes: 0

Amit
Amit

Reputation: 46361

Can be done with the following recursive CTE:

WITH cte AS (
  SELECT *,
    CAST(ROW_NUMBER() OVER(ORDER BY id) AS REAL) rn,
    1 level
  FROM tbl
  WHERE parent = 0
  UNION ALL
  SELECT t2.*,
    cte.rn + (CAST(ROW_NUMBER() OVER(ORDER BY t2.id) AS REAL) / POWER(10, cte.level)) rn,
    cte.level + 1 level
  FROM tbl t2 INNER JOIN cte
    ON t2.parent = cte.id
)
SELECT id, parent, type
FROM cte
ORDER BY rn

See SQLFiddle with more complicated sample data (deeper hierarchies, "unordered parent-child id's")

Upvotes: 8

Related Questions