如何用递归CTE查询替换COALESCE?

时间:2018-02-13 23:47:20

标签: sql sql-server common-table-expression recursive-query

Recursive CTE with three tables的后续问题帮助我了解了SQL Server中的CTE。

最初的问题有什么变化?表MANAGERS不再包含没有经理的org.units行。

目标是获得组织单位的第一个非空管理员。我已经使用COALESCE和OUTER JOIN了解它,我的问题是,是否可以使用递归查询?

示例代码如下。

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

预期结果

我希望3的MANAGER列显示" Jane Doe"。例如,COALSECE(上面的最后一个SELECT)就是这样做的:

ORGID   NAME    MANAGER
1       One     John Doe
2       Two     Jane Doe
3       Three   Jane Doe

1 个答案:

答案 0 :(得分:1)

@ZLK在他/她的评论中是正确的,你不需要MANAGERS表来执行递归:

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

结果:

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