Sql 递归cte-标记所有叶

Sql 递归cte-标记所有叶,sql,tsql,recursion,sql-server-2008-r2,common-table-expression,Sql,Tsql,Recursion,Sql Server 2008 R2,Common Table Expression,我有一个可以正常工作的递归CTE,但我还需要一件事:为每个结果添加[isLeaf]标志,这将告诉记录是否有更多的子项[Leafs]字段和子项计数器会更好 工作示例粘贴在下面。它统计每个类别的级别并将名称连接到类别路径中,但sql server不允许在CTE的递归部分中使用左连接、TOP、SELECT DISTINCT、聚合和子查询,这些都是我需要的方法 DROP TABLE cats GO create table cats( catid int primary key clustere

我有一个可以正常工作的递归CTE,但我还需要一件事:为每个结果添加[isLeaf]标志,这将告诉记录是否有更多的子项[Leafs]字段和子项计数器会更好

工作示例粘贴在下面。它统计每个类别的级别并将名称连接到类别路径中,但sql server不允许在CTE的递归部分中使用左连接、TOP、SELECT DISTINCT、聚合和子查询,这些都是我需要的方法

DROP TABLE cats
GO
create table cats(
    catid int primary key clustered,
    parent int, --parent's catid. 0 for top-level entries
    name    varchar(255)
)
GO

insert into cats (catid, parent, name)
select 1 as catid, 0 as parent, 'computers' as name union all 
    select 2, 1, 'laptops' union all 
        select 4, 2, 'ibm' union all 
        select 5, 2, 'others' union all 
    select 3, 1, 'desktops' union all 
        select 6, 3, 'amd' union all 
        select 7, 3, 'others' union all 
select  8, 0 , 'cars' union all 
    select 9, 8, 'others' union all 
    select 10, 8, 'pickups' union all 
        select 11, 10, 'others' union all 
        select 12, 10, 'ford' union all 
            select 14, 12, 'ranger' union all 
            select 15, 12, 'others'
GO      

;with cteCat as (
    select 
        CatId, Parent,
        [cteLevel]  = 1,
        [ctePath]   = cast(Name as varchar(4000))
        --,[cteIsLeaf]  = 0
    from cats
    where 1=1
        and Parent=0
union all 
    select 
        c.CatId, c.Parent,
        [cteLevel] = cc.cteLevel+1,
        [ctePath] = cast(cc.ctePath + ' | ' + c.Name as varchar(4000))
        --,[cteIsLeaf]  = 0 --???--
    from cats c
    join cteCat cc
        on c.Parent = cc.CatId
    where 1=1
        and c.Parent<>0
)
select 
    * 
from cteCat
order by 
    catid

最容易实现的事情就是在检查IsLeaf的最终select语句中添加相关子查询。这是一个简单的检查,看看特定的CatID是否是某人的父母。不需要递归

SELECT
    * ,  CASE WHEN EXISTS (SELECT * FROM cats c2 WHERE c2.parent = c1.CatID) THEN 0 ELSE 1 END AS IsLeaf
FROM cteCat c1
ORDER BY
    catid
编辑: 如果你需要[Leafs]作为即将出生的孩子的计数,那么也很容易得到它们:

SELECT
    * 
    , CASE WHEN EXISTS (SELECT * FROM cats c2 WHERE c2.parent = c1.CatID) THEN 0 ELSE 1 END AS IsLeaf
    , (SELECT COUNT(*) FROM cats c2 WHERE c2.parent = c1.CatID) AS Leafs
FROM cteCat c1
ORDER BY 
    c1.catid

但是,如果你需要[Leafs]作为儿童和所有chieldren儿童的总计数器,则需要重写CTE,使其自下而上,而不是自上而下

从Sql Server 2008开始,您可以使用一种特殊的数据类型来处理分层数据。@Nikola Markovinović非常好的提示,谢谢!我不知道这种类型的存在,但现在我可以在我的项目中看到许多应用程序。