对祖父母的CTE递归查询

时间:2020-06-15 23:40:02

标签: sql sql-server select subquery recursive-query

我有下表:

ID NAME PARENT_ID ISDELETED ISINEDIT
1  JJ     NULL     1          0
2  AR     1        0          0
3 PR      2        0          0
4 DR      NULL     0          1

我需要获取的是SELECT查询,该查询将仅返回具有ISDELETED 0和ISINEDIT 0且其父母或祖父母也为0的行

我目前有:

;WITH ChildParent AS
(
    SELECT
        a.id,
        a.name,
        a.isinedit,
        a.parent_id,
        a.isdeleted
    FROM dbo.table 
    WHERE isdeleted = 0 AND isinedit = 0 

    UNION ALL

    SELECT
        a.id,
        a.name,
        a.isinedit,
        a.parent_id,
        a.isdeleted
    FROM dbo.table a
    INNER JOIN ChildParent cp ON a.parent_id = cp.id
    WHERE a.isdeleted = 0  AND a.isinedit = 0
)
SELECT  
        id,
        name,
    parent_id,
        isinedit,
        isdeleted
FROM ChildParent

但是由于某种原因,它会返回双行

3 个答案:

答案 0 :(得分:1)

您需要向isdeleted = 0 AND isinedit = 0源添加相同的INNER JOIN childParent CP谓词。

...但是,如果您这样做的话,您会非常轻率地进行CTE查询,并且如果必须一遍又一遍地重复同一件事,那么可能会有更好的方法。

...有! SELECT查询可以具有多个CTE表达式:

;
WITH filtered AS
(
    SELECT
        a.id,
        a.name,
        a.parent_id,
    FROM
        dbo.Table
    WHERE
        IsDeleted = 0
        AND
        IsInEdit = 0 
)

WITH cte AS
(
    SELECT
        a.id,
        a.name,
        a.parent_id
    FROM
        filtered

    UNION ALL

    SELECT
        a.id,
        a.name,
        a.parent_id
    FROM
        filtered
        INNER JOIN cte ON a.parent_id = cte.id
)
SELECT  
    *
FROM
    cte
ORDER BY
    id

答案 1 :(得分:1)

我认为这比您想象的要复杂。据我所知,您需要首先遍历每个节点的整个父级层次结构,然后然后检查是否有任何父级不满足规则(您可以通过在之后停止进行一些优化) 遇到了第一个不符合要求的父母。

您还需要跟踪原始节点,以便可以在外部查询中进行正确过滤(这样可以避免当前获取的重复项)。

我的查询短语为:

with cte as (
    select 
        t.*, 
        t.id original_id, 
        0 lvl, 
        1 is_ok
    from dbo.table t
    where isdeleted = 0 and isinedit = 0 
    union all
    select 
        t.*, 
        c.original_id, 
        c.lvl + 1, 
        case when t.isdeleted = 0 and t.isinedit = 0 then 1 else 0 end
    from dbo.table t
    inner join cte c on c.parent_id = t.id
    where c.is_ok = 1
)
select *
from cte c
where 
    c.lvl = 0
    and not exists (
        select 1 from cte c1 where c1.original_id = c.original_id and c1.is_ok = 0
    )

请注意,无论树中存在多少级别,该查询都将起作用(如果您有100多个级别,则需要在查询末尾添加option(maxrecursion 0)

答案 2 :(得分:0)

如果您首先在CTE中创建干净的数据集,则这些类型的查询会更容易。它消除了所有CTE查询中的多余过滤子句。

在这种情况下,返回所有未删除且不在编辑中的行。接下来将获取您的数据。祖父母INNER JOIN父母INNER JOIN CHILD

;WITH GoodDataRows AS
(
    SELECT
        a.id,
        a.name,
        a.isinedit,
        a.parent_id,
        a.isdeleted
    FROM dbo.table 
    WHERE isdeleted = 0 AND isinedit = 0 
)
SELECT
  *
FROM GoodDataRows gp
  INNER JOIN GoodDataRows p ON p.parent_id = gp.id
  INNER JOIN GoodDataRows c on c.parent_id = p.id