Reputation: 101
I don't think I understand recursive queries well. The classic recursive query I see is the manager and employee recursive query. I'm using that query to capture the entire hierarchy, however I would like to make a query that can enable me to captures all parents, children and their levels (relative to the top level). I feel like I need a recursion on a recursive query?
WITH RECURSIVE
tree AS (
SELECT
id_employee,
id_manager,
0 AS level
FROM
people
WHERE
id_manager = '00001'--this is the boss of all bosses
UNION ALL
SELECT
p.id_employee,
p.id_manager,
t.level + 1 AS level
FROM
(
SELECT
id_employee,
id_manager
FROM
people
) p
JOIN tree t ON p.manager_id = t.employee_id
)
SELECT *
FROM
tree;
So right now, since I'm getting the top-level boss 00001
, I will get everyone in the organization. But if I add a WHERE id_manager =
clause on the last line for someone in the middle of the hierarchy, I'll just get their direct reports. I'd like to retrieve all direct reports below that manager, but also the reports below those managers.
Is there a way to modify this build/modify this query to maintain the entire hierarchy above but also be able to query it to identify someone in the middle of it, all their children and as well as their relative level to the top-level boss?
Thanks!
Upvotes: 1
Views: 1749
Reputation: 2060
Edit: As you commented below, it turned out your problem on the level was what I could not understand.
As you have probably understood, a recursive CTE is divided into 2 parts.
WITH RECURSIVE MyCTE AS (
<Start of the recursion/loop>
UNION
<Step from level N to level N+1/N-1>
)
We will:
Postgresql only allows 1 recursive CTE (not sure for other DBMS), so we will need to do 2 and 3 together. We only need a little extra caution to allow the query to start with several starting point (id_manager IN (...)
)
WITH RECURSIVE tree(id_root_manager, id_direct_manager, id_employee, level) AS (
SELECT id_manager,
id_manager,
id_employee,
UNNEST(ARRAY[0,1]) /* level > 0: go to the bottom, level <= 0: go to the top of the hierarchy */
FROM people
WHERE id_manager IN ('00555')
UNION ALL
SELECT id_root_manager,
id_manager,
p.id_employee,
CASE WHEN level <= 0 THEN level-1 ELSE level+1 END
FROM people p
JOIN tree t ON (level > 0 AND p.id_manager = t.id_employee) OR (level <= 0 AND t.id_direct_manager = p.id_employee)
)
SELECT id_root_manager, id_direct_manager, id_employee, level - (SELECT MIN(level) FROM tree WHERE id_root_manager = h.id_root_manager) AS level
FROM tree h
WHERE level > 0
ORDER BY id_root_manager, level
If you are not interested in the "root" manager, you may want to avoid duplicates by changing the final select to:
SELECT DISTINCT id_direct_manager, id_employee, level - (SELECT MIN(level) FROM tree WHERE id_root_manager = h.id_root_manager) AS level
FROM tree h
WHERE level > 0
ORDER BY level
Upvotes: 2