简化 CTE 查询

simplify CTE query

我有一种数据结构,其中各种数据元素可以相互关联。一个记录可以是多个子记录的父记录,但一个子记录只能有一个父记录。我正在处理查询以检查这些记录 'families' 以查看它们是否都标记为过时,以便我可以删除它们。

为了解决这个问题,我编写了一个 CTE,它从最底层的子记录开始,向上处理树,记录状态,最终在顶层记录中得到 'family' 状态。

我 运行 使用这种方法遇到的一个问题是当一个记录有多个子记录时,我最终得到了顶级父记录的多个记录。在某些情况下,一条子路径可能符合删除条件,但另一条则不行。我无法想出一种在 CTE 中识别它的方法,并以一个子查询结束,我在其中计算符合删除条件和不符合删除条件的路径数量,然后只有 return 记录 > 1 'delete' 个路径和 0 个“不要删除”路径。

这是我的代码,包含示例数据

create table #demand (id int, [status] varchar(10))
create table #relation (parent int, child int)

insert into #demand values (1, 'active')
insert into #demand values (2, 'obsolete')
insert into #demand values (3, 'obsolete')
insert into #demand values (4, 'obsolete')
insert into #demand values (5, 'active')
insert into #demand values (6, 'obsolete')

insert into #relation values(2, 3)
insert into #relation values(2, 4)
insert into #relation values(4, 5)

--the CTE splitFamily traverses the split family records from bottom up, checking that each record is Obsolete. If any record is not
;with
splitFamily as (
    select 
        D.id, 
        DR.child,
        case when D.[status] = 'obsolete' then 'Y' else 'N' end as CanDelete
    from #demand D
    left outer join #relation DR on D.id = DR.parent
    where DR.child is null

    union all

    select
        D.id, 
        DR.child,
        case when D.[status] = 'obsolete' and splitFamily.CanDelete = 'Y' then 'Y' else 'N' end as CanDelete
    from
        splitFamily
    join #relation DR on splitFamily.id = DR.child
    join #demand D  on DR.parent = D.id
)
select id from (
    select parentLevel.id,
    sum(case when parentLevel.CanDelete = 'Y' then 1 else 0 end) as "Y",
    sum(case when parentLevel.CanDelete = 'N' then 1 else 0 end) as "N"
    from splitFamily parentLevel
    --The following join ensures we only return top level parent records to be deleted. 
    left join splitFamily children on parentLevel.id = children.child
    where children.id is null
    group by parentLevel.id
) as splits where Y > 0 and N = 0

drop table #demand
drop table #relation

此代码有效,并输出记录 6 作为唯一符合条件的删除记录。如果记录 5 更改为 'obsolete',则查询在结果中正确地包含 2。

我的问题是关于是否有一种更清晰、更清晰的方法来识别 CTE 中的那些拆分路径,以避免额外的子查询和路径计数。对我来说,这混淆了代码的目的,并使继续维护变得更加困难。

我最终将我的最终 select 更改为以下内容。使用 window 函数,我能够避免令人困惑的求和,并且在我看来,select 现在更加清晰了。 FIRST_VALUE 函数确保如果任何行的每个 id 都有 N,则返回 'N'。

select distinct id from (
    select parentLevel.id,
    FIRST_VALUE(parentLevel.CanDelete) over(partition by parentLevel.DemandId order by parentLevel.CanDelete) as CanDelete
    from splitFamily parentLevel
    left join splitFamily children on parentLevel.id = children.child
    where children.id is null
) as splits where CanDelete = 'Y'