简体   繁体   中英

SQL Server: how to achieve the tree nested level sort

I have tree structure based records(parent and child relationship) and I want to achieve nested level sort. Basically, I need level based sort as well as need to maintain the parent and child relationship(sort child level). I have tried using CTE, but I could achieve only level based sort but problem is can't achieve the parent and child relationship.

I tried below query

SELECT * FROM EMP WHERE PARENTID IS NULL order by

CASE WHEN PARENTID IS NULL THEN 
    lower(name)
ELSE 
    (SELECT lower(name) FROM EMP WHERE ID=ROOTID) END  DESC,
ROOTID, JOBLEVEL, NAME 

Actual TABLE

ID       PARENTID       NAME        FULLPATH   LEVEL  ROOTID
_____________________________________________________________               
1        NULL            AA             1        1      1
2        1               BB            1.2       2      1
3        1               ZZ            1.3       2      1
4        1               HH            1.4       2      1
5        2               RR            1.2.5     3      1
6        2               CC            1.2.6     3      1
7        3               DD            1.3.7     3      1
8        3               UU            1.3.8     3      1
9        4               GG            1.4.9     3      1
10       4               LL            1.4.10    3      1
11      NULL             KK            11        1      11
12      11               VV            11.12     2      11

Actual Tree structure

AA
    BB
    ZZ
    HH
        RR
        CC
        DD
        UU
        GG
        LL
KK
    VV

Expected ASCENDING ORDER OF TABLE

ID   PANTID     NAME    FULLPATH      LEVEL
____________________________________________            
1       NULL    AA          1           1

2       1       BB          1.2         2
6       2       CC          1.2.6       3
5       2       RR          1.2.5       3

4       1       HH          1.4         2
9       4       GG          1.9.4       3
10      4       LL          1.4.10      3

3       1       ZZ          1.3         2
7       3       DD          1.3.7       3
8       3       UU          1.3.8       3

11      NULL    KK          11          1
12      11      VV          11.12       2

Expected ASCENDING TREE STRUCTURE

AA  
    BB  
        CC
        RR
    HH
        GG
        LL
    ZZ
        DD
        UU
KK
        VV

Expected DESCENDING ORDER OF TABLE

ID  PARENTID        NAME     FULLPATH     LEVEL
________________________________________________
11      NULL        KK          11          1
12      11          VV          11.12       2

1       NULL        AA          1           1

3       1           ZZ          1.3         2
7       3           DD          1.3.7       3
8       3           UU          1.3.8       3

4       1           HH          1.4         2
9       4           GG          1.9.4       3
10      4           LL          1.4.10      3

2       1           BB          1.2         2
6       2           CC          1.2.6       3
5       2           RR          1.2.5       3

Expected DESCENDING TREE STRUCTURE

KK
    VV
AA  
    ZZ
        UU
        DD
    BB  
        RR
        CC          
    HH
        LL
        GG

Is FULLPATH and actual field? You could use some clever string manipulation to convert it to a string that sorts exactly like you want (for example replacing all integers with a 6,7,8, however many you expect to need zero padded version). For example if you know you will never reach 1 million IDs turn 1 to 00000001, 1.2 to 0000001.0000002 etc. That should sort exactly as you want.

However this will sort same level IDs by ID and not by Name. If that is part of your specification which you do not mention but your example seems to do so, it will not work.

The only other ways I can think of have to do with assumption of a reasonable max level of nesting. In that case you would break each level into its own sub-query or view and re-integrate as linked tables.

Recursive CTE is the tool you should use. Here is working example for you. Note that your FULLPATH, LEVEL, and ROOTID fields are redundant.

declare @emp table (id int,parentId int, name varchar(10))--your table
insert @emp values                --test values
(1   ,     NULL    ,        'AA'), 
(2,        1,               'BB'),
(3,        1,               'ZZ'), 
(4,        1,               'HH'), 
(5,        2 ,              'RR'), 
(6,        2,               'CC'), 
(7,        3,               'DD'), 
(8,        3,               'UU'), 
(9,        4,               'GG'), 
(10,       4,               'LL'), 
(11,      NULL       ,      'KK'), 
(12,      11,               'VV') 

;with emps as (
--anckor query
select id, parentid, name, 
format(id,'00000') sort, --format is sql 2012+ feature
1 lvl, --calc levels
format(100000-id,'00000') descsort --trick for desc sort
from @emp where parentId is null
union all --must be ALL
--recursive query
select e.id, e.parentid, e.name, sort+format(e.id,'00000') sort,  lvl+1,descsort+format(e.id,'00000')
from @emp e 
inner join emps on e.parentId=emps.id
)
select * from emps
order by sort  --or **by descsort** if you want "desc"

Edit for pre-2012 version

stuff('00000',5-len(cast(id as varchar)),1000,cast(id as varchar)) 

update to sort by names as well

;with emps as (
select id, parentid, name, 
cast(left(name,2)+stuff('00000',5-len(cast(id as varchar)),1000,cast(id as varchar)) as varchar(max)) sort, --2008
--format(id,'00000') sort, 
1 lvl, 
format(100000-id,'00000') descsort -- use sort field as example
from @emp where parentId is null
union all
select e.id, e.parentid, e.name, 
sort+left(e.name,2)+stuff('00000',5-len(cast(e.id as varchar)),1000,cast(e.id as varchar)),
--sort+format(e.id,'00000') sort,  
lvl+1,descsort+format(e.id,'00000')
from @emp e 
inner join emps on e.parentId=emps.id
)
select * from emps
order by sort -- descsort 

This is for desc sort

;with emps as (
select id, parentid, name, 
cast(left(name,2)+stuff('00000',5-len(cast(id as varchar)),1000,cast(id as varchar)) as varchar(max)) sort, --2008
--format(id,'00000') sort, --2012
1 lvl, 
cast(stuff('00000',6-len(cast((100000-id) as varchar)),1000,cast((100000-id) as varchar))+left(name,2) as varchar(max)) descsort --2008
--format(100000-id,'00000') descsort -- use sort field as example
from @emp where parentId is null
union all
select e.id, e.parentid, e.name, 
sort+left(e.name,2)+stuff('00000',5-len(cast(e.id as varchar)),1000,cast(e.id as varchar)),
--sort+format(e.id,'00000') sort,  
lvl+1,
descsort+stuff('00000',6-len(cast(100000-e.id as varchar)),1000,cast(100000-e.id as varchar))+left(e.name,2)
--format(e.id,'00000')
from @emp e 
inner join emps on e.parentId=emps.id
)
select * from emps
order by --sort desc-- 
descsort 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM