Slavo
Slavo

Reputation: 41

Order parent child records by parent group and children

I need sort query results by specific two related columns. My table is:

Row no | Col 1 | Col 2 | Col 3 | Col 4
    1  |   1   |  X    |  1    |  5
    2  |   2   |  Y    |  1    |  6
    3  |   5   |  Z    |  2    |  7
    4  |   6   |  T    |  2    |  0
    5  |   7   |  T    |  3    |  0
    6  |   6   |  W    |  2    |  0

The values in Col 4 represents the child record linked to Col 1.

So for Row no = 1 the next child record is row 3, where Col 1 holds the value of Col 4 from the first row.

The next child row for row 3 is row 5, based on the link between Col 1 and Col 4.

And I'd like to return this results:

Row no | Col 1 | Col 2 | Col 3 | Col 4
    1  |   1   |  X    |  1    |  5
    3  |   5   |  Z    |  2    |  7
    5  |   7   |  T    |  3    |  0    
    2  |   2   |  Y    |  1    |  6
    4  |   6   |  T    |  2    |  0
    6  |   6   |  W    |  2    |  0 

So I want the ordering to show a Parent row, followed by it's child rows, before moving on to the next top level Parent row.

Upvotes: 4

Views: 1624

Answers (1)

Tanner
Tanner

Reputation: 22753

You can achieve what you're after with a Recursive CTE to find all the parent records and link them to their child records.

Dummy table setup:

CREATE TABLE #Table1
    (
      [Row no] INT ,
      [Col 1] INT ,
      [Col 2] VARCHAR(1) ,
      [Col 3] INT ,
      [Col 4] INT
    );

INSERT  INTO #Table1
        ( [Row no], [Col 1], [Col 2], [Col 3], [Col 4] )
VALUES  ( 1, 1, 'X', 1, 5 ),
        ( 2, 2, 'Y', 1, 6 ),
        ( 3, 5, 'Z', 2, 7 ),
        ( 4, 6, 'T', 2, 0 ),
        ( 5, 7, 'T', 3, 0 ),
        ( 6, 6, 'W', 2, 0 );

Recursive CTE:

;WITH    cte
          AS ( SELECT   * ,
                        ROW_NUMBER() OVER ( ORDER BY t1.[Col 1] ) GroupNo
               FROM     #Table1 t1
               WHERE    t1.[Col 1] NOT IN ( SELECT  [Col 4] FROM #Table1 )
               UNION ALL
               SELECT   t.* ,
                        cte.GroupNo
               FROM     #Table1 t
                        INNER JOIN cte ON cte.[Col 4] = t.[Col 1]
             )
    SELECT  *
    FROM    cte
    ORDER BY cte.GroupNo , cte.[Row no]

DROP TABLE #Table1

This combines 2 queries with a UNION ALL. The first query finds the top level items where the value of [Col 1] does not appear in [Col 4]:

WHERE    t1.[Col 1] NOT IN ( SELECT  [Col 4] FROM #Table1 )

The second query finds the child records on the first query with this JOIN:

INNER JOIN cte ON cte.[Col 4] = t.[Col 1]

For the ordering, I've used the following to give the the results of the first query a GroupNo, which is used later to order the records:

ROW_NUMBER() OVER ( ORDER BY t1.[Col 1] ) GroupNo 

Upvotes: 1

Related Questions