Reputation: 92
A follow-up question to Recursive CTE with three tables which helped me with CTE's in SQL Server.
What has changed from the initial question? The table MANAGERS
no longer includes rows for org.units that have no manager.
The goal is to get the first non-null manager for a organizational unit. I've got it working using COALESCE and OUTER JOIN's, my question is if it is possible to use a recursive query instead?
Example code follows below.
DECLARE @ORG_PARENTS TABLE (ORG_ID INT, ORG_PARENT INT )
DECLARE @MANAGERS TABLE (ORG_ID INT, MANAGER VARCHAR(100))
DECLARE @ORG TABLE (ORG_ID INT, ORG_NAME VARCHAR(100))
INSERT @ORG (ORG_ID, ORG_NAME)
VALUES (1, 'One')
, (2, 'Two')
, (3, 'Three')
INSERT @ORG_PARENTS (ORG_ID, ORG_PARENT)
VALUES (1, NULL)
, (2, 1)
, (3, 2)
INSERT @MANAGERS (ORG_ID, MANAGER)
VALUES (1, 'John Doe')
, (2, 'Jane Doe')
;
-- The original answer
WITH BOSS
AS
(
SELECT m.MANAGER, m.ORG_ID AS ORI, m.ORG_ID, p.ORG_PARENT, 1 cnt
FROM @MANAGERS m
INNER JOIN @ORG_PARENTS p
ON p.ORG_ID = m.ORG_ID
UNION ALL
SELECT m1.MANAGER, b.ORI, m1.ORG_ID, OP.ORG_PARENT, cnt +1
FROM BOSS b
INNER JOIN @ORG_PARENTS AS OP
ON OP.ORG_ID = b.ORG_PARENT
INNER JOIN @MANAGERS m1
ON m1.ORG_ID = OP.ORG_ID
)
--SELECT *
--FROM BOSS
--WHERE ORI = 3
-- The following query only returns ORG "One" and "Two"
SELECT ORG.ORG_ID as "ORGID", ORG.ORG_NAME AS "NAME", MGR.MANAGER AS "MANAGER"
FROM @ORG ORG INNER JOIN @MANAGERS MGR ON MGR.ORG_ID = ORG.ORG_ID
-- The following query returns three rows, with NULL as the MANAGER for "Three"
-- My goal is to get the manager from the first parent that is not null
SELECT ORG.ORG_ID as "ORGID", ORG.ORG_NAME AS "NAME", (SELECT MANAGER FROM @MANAGERS M WHERE M.ORG_ID = ORG.ORG_ID) AS "MANAGER"
FROM @ORG ORG
-- The following workaround works, I can use COALESCE and a large number of OUTER JOINs to get the desired effect.
-- Is it possible to replace the code below with a recursive query, optimally where the number of levels is not hardcoded?
SELECT ORG.ORG_ID as "ORGID", ORG.ORG_NAME AS "NAME",
COALESCE(
(SELECT MANAGER FROM @MANAGERS M WHERE M.ORG_ID = ORG.ORG_ID),
(SELECT MANAGER FROM @MANAGERS M WHERE M.ORG_ID = p1.ORG_PARENT),
(SELECT MANAGER FROM @MANAGERS M WHERE M.ORG_ID = p2.ORG_PARENT)
) AS "MANAGER"
FROM @ORG ORG
LEFT OUTER JOIN @ORG_PARENTS p1 ON ORG.ORG_ID = p1.ORG_ID
LEFT OUTER JOIN @ORG o1 ON p1.ORG_PARENT = o1.ORG_ID
LEFT OUTER JOIN @ORG_PARENTS p2 ON o1.ORG_ID = p2.ORG_ID
LEFT OUTER JOIN @ORG o2 ON p2.ORG_PARENT = o2.ORG_ID
Expected result
I want the MANAGER column for 3 to show "Jane Doe". For example the COALSECE (last SELECT above) does that:
ORGID NAME MANAGER
1 One John Doe
2 Two Jane Doe
3 Three Jane Doe
Upvotes: 1
Views: 1120
Reputation: 15140
@ZLK is right in his/her comment, you don't need the MANAGERS table to execute the recursion:
WITH BOSS
AS
(
SELECT P.ORG_ID AS ORI, P.ORG_ID, p.ORG_PARENT, 1 cnt
FROM @ORG_PARENTS p
UNION ALL
SELECT b.ORI, OP.ORG_ID, OP.ORG_PARENT, cnt +1
FROM BOSS b
INNER JOIN @ORG_PARENTS AS OP
ON OP.ORG_ID = b.ORG_PARENT
)
SELECT *
FROM BOSS
LEFT JOIN @MANAGERS m1
ON m1.ORG_ID = BOSS.ORG_ID
WHERE ORI = 3
Result:
+-----+--------+------------+-----+--------+----------+
| ORI | ORG_ID | ORG_PARENT | cnt | ORG_ID | MANAGER |
+-----+--------+------------+-----+--------+----------+
| 3 | 3 | 2 | 1 | NULL | NULL |
| 3 | 2 | 1 | 2 | 2 | Jane Doe |
| 3 | 1 | NULL | 3 | 1 | John Doe |
+-----+--------+------------+-----+--------+----------+
Upvotes: 1