Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find all the supervisors of an employee

I know the answer would seem to be to use "WITH RECURSIVE" as per this post but I'm just not getting it.

I have a table called people, and a table called position_hierarchy. The people table has a unique id uperson_id and position id we call pcn and an enabled flag (because when somebody leaves and is replaced, their replacement gets the same pcn). The position_hierarchy has the column pcn, and another column reports_to which is the pcn of the person above them in the hierarchy. What I want to do is give a person's uperson_id and find all the uperson_ids of the people above them in the hierarchy, and/or give a uperson_id and another person's uperson_id and tell if the second person somebody who has a supervisory position over the first.

The president of the company is indicated because their pcn is the same as their reports_to. (Not my decision - I would have used a null reports_to)

What I came up with so far is:

with recursive parents (uperson_id, pcn, reports_to) as
(
 select p1.uperson_id, ph1.pcn, ph1.reports_to
 from people p1
 join position_hierarchy ph1 on ph1.pcn = p1.pcn
 where reports_to != ph1.pcn and active_revoke_flag = '0'

 union all

 select p2.uperson_id, ph2.pcn, ph2.reports_to
 from people p2
 join position_hierarchy ph2 on p2.pcn = ph2.pcn
 join parents pp on pp.pcn = ph2.reports_to
)
select parents.* from parents where uperson_id = 'aaa3644';

but that returns 5 rows with the same uperson_id, pcn and reports_to (which seems like the right number of rows, but I want the supervisor's uperson_id at each level. I feel like I'm missing something very basic, and I'll probably slap my head when you tell me what I'm doing wrong.

What I did

Based on Erwin Brandstetter's answer, I fixed a few things (mostly because I didn't make clear which table the active_revoke_flag was in) and came up with:

with recursive p as (
    select pcn, reports_to
    from   position_hierarchy
    where  pcn = (SELECT pcn FROM people WHERE uperson_id = 'aaa3644')
    union all
    select ph2.pcn, ph2.reports_to
    from   p
    join   position_hierarchy ph2 ON ph2.pcn = p.reports_to AND
           p.pcn != p.reports_to
)
select p2.uperson_id, p2.active_revoke_flag, p.*
from   p
join   people p2 USING (pcn)
where  p2.active_revoke_flag = '0';
like image 223
Paul Tomblin Avatar asked Sep 01 '12 20:09

Paul Tomblin


1 Answers

I would try this bottom up approach, start with the person of interest and work my way up:

with recursive p as (
    select p1.uperson_id, p1.pcn, ph1.reports_to
    from   people p1
    join   position_hierarchy ph1 USING (pcn)
    where  ph1.active_revoke_flag = '0'
    and    p1.uperson_id = 'aaa3644'

    union all

    select p2.uperson_id, p2.pcn, ph2.reports_to
    from   p
    join   position_hierarchy ph2 ON ph2.pcn = p.reports_to
                                 AND ph2.active_revoke_flag = '0'
    join   people p2 ON p2.pcn = ph2.pcn
)
select * from p;

Or, faster, because we only join to person once:

with recursive p as (
    select pcn, reports_to
    from   position_hierarchy
    where  active_revoke_flag = '0'
    and    pcn = (SELECT pcn FROM person WHERE uperson_id = 'aaa3644')

    union all

    select ph2.pcn, ph2.reports_to
    from   p
    join   position_hierarchy ph2 ON ph2.pcn = p.reports_to
                                 AND ph2.active_revoke_flag = '0'
)
select p2.uperson_id, p.*
from   p
join   people p2 USING (pcn); -- assuming pcn is unique in table person

As an aside: I do find your design with duplicate pcn somewhat dubious.

like image 56
Erwin Brandstetter Avatar answered Sep 20 '22 15:09

Erwin Brandstetter