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';