How to replace COALESCE with recursive CTE query?

天涯浪子 提交于 2019-12-11 09:53:25

问题


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 MANAGERSno 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

回答1:


@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 |
+-----+--------+------------+-----+--------+----------+


来源:https://stackoverflow.com/questions/48777510/how-to-replace-coalesce-with-recursive-cte-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!