Search code examples
sqlsql-serverrecursioncommon-table-expressionhierarchy

SQL recursive query to get department code


I need to generate a list of users' departments codes. If a user doesn't have code get its manager code, and so on up the hierarchy.

The initial table looks like this:

manager emp     code
-----------------------
boss    subboss AAA
boss    subsub  SUBCODE
subboss john    ABC
subboss alan    (null)
(null)  boss    ZZZ
subsub  steve   (null)
steve   rick    (null)

CREATE TABLE Users
(
     [manager] varchar(10), 
     [emp] varchar(10), 
     [code] varchar(10)
);
    
INSERT INTO Users ([manager], [emp], [code])
VALUES
    ('boss', 'subboss', 'AQQ'),
    ('boss', 'subsub', 'SUBSUB'),
    ('subboss', 'john', 'ABC'),
    ('subboss', 'alan', null),
    (null, 'boss', 'ZZZ'),
    ('subsub', 'steve', null),
    ('steve', 'rick', null);

Desired result is this:

manager emp     code
------------------------
boss    subboss AAA
boss    subsub  SUBCODE
subboss john    ABC
subboss alan    AAA
(null)  boss    ZZZ
subsub  steve   SUBCODE
steve   rick    SUBCODE

My first attempt is:

select 
    manager, emp,
    coalesce(code, (select code from Users u1 where u.manager = u1.code))
from 
    Users u;

But it returns only the direct manager code.

I'd appreciate tips on how to do it recursively with CTE.


Solution

  • Try something like this:

    WITH Hierarchy AS
    (
        -- create the "anchor" - the toplevel node(s)
        SELECT
            u.emp, u.manager, u.code, 0 AS Level
        FROM
            Users u
        WHERE
            u.manager IS NULL
    
        UNION ALL
    
        -- recursive part - join subordinate to manager, one level up
        SELECT
            u.emp, u.manager, COALESCE(u.code, h.code), h.Level + 1
        FROM
            Hierarchy h
        INNER JOIN
            Users u ON u.manager = h.emp
    )
    SELECT
        *
    FROM
        Hierarchy
    

    This results in a dataset like this:

    emp     manager code    Level
    -----------------------------
    boss    NULL    ZZZ      0
    subboss boss    AQQ      1
    subsub  boss    SUBSUB   1
    steve   subsub  NULL     2
    rick    steve   NULL     3
    john    subboss ABC      2
    alan    subboss NULL     2