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_id
s 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';
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With