Sql 如何遍历id为&;的表中的路径;家长号?

Sql 如何遍历id为&;的表中的路径;家长号?,sql,sql-server,hierarchical-data,recursive-query,Sql,Sql Server,Hierarchical Data,Recursive Query,假设我有一张像这样的桌子: id | parentId | name 1 NULL A 2 1 B 3 2 C 4 1 E 5 3 E 我正在尝试编写一个标量函数,可以调用为: 选择dbo.GetId('A/B/C/E'),如果我们使用上述参考表,它将生成“5”。该函数将执行以

假设我有一张像这样的桌子:

id    |   parentId   |  name
 1          NULL         A
 2            1          B
 3            2          C
 4            1          E
 5            3          E
我正在尝试编写一个标量函数,可以调用为:

选择dbo.GetId('A/B/C/E')
,如果我们使用上述参考表,它将生成“5”。该函数将执行以下步骤:

  • 查找“A”的ID,该ID为1
  • 查找父项为“A”(ID:1)的“B”的ID,该ID将为ID:2
  • 查找父项为“B”(ID:2)的“C”的ID,该ID将为ID:3
  • 查找父项为“C”(ID:3)的“E”的ID,该ID将为ID:5

  • 我试着做一个WHILE循环,但它变得非常复杂,非常快。。。我只是想一定有一个简单的方法来做到这一点。

    我想我是基于@SeanLange的建议,使用递归CTE(见上面的评论):


    我想我是基于@SeanLange建议使用递归CTE(见上面的评论)得到的:


    这是一个基于我所了解的样本数据和需求的功能性rcte示例

    if OBJECT_ID('tempdb..#Something') is not null
        drop table #Something
    
    create table #Something
    (
        id int
        , parentId int
        , name char(1)
    )
    
    insert #Something
    select 1, NULL, 'A' union all
    select 2, 1, 'B' union all
    select 3, 2, 'C' union all
    select 4, 1, 'E' union all
    select 5, 3, 'E'
    
    declare @Root char(1) = 'A';
    
    with MyData as
    (
        select *
        from #Something
        where name = @Root
    
        union all
    
        select s.*
        from #Something s
        join MyData d on d.id = s.parentId
    )
    
    select *
    from MyData
    

    请注意,如果更改变量的值,输出将进行调整。我想让它成为一个内联表值函数。

    这里是一个基于示例数据和我所理解的需求的函数rcte示例

    if OBJECT_ID('tempdb..#Something') is not null
        drop table #Something
    
    create table #Something
    (
        id int
        , parentId int
        , name char(1)
    )
    
    insert #Something
    select 1, NULL, 'A' union all
    select 2, 1, 'B' union all
    select 3, 2, 'C' union all
    select 4, 1, 'E' union all
    select 5, 3, 'E'
    
    declare @Root char(1) = 'A';
    
    with MyData as
    (
        select *
        from #Something
        where name = @Root
    
        union all
    
        select s.*
        from #Something s
        join MyData d on d.id = s.parentId
    )
    
    select *
    from MyData
    

    请注意,如果更改变量的值,输出将进行调整。我会将其设置为一个内联表值函数。

    CTE
    版本不是获取分层数据的最佳方式。()

    你应该做下面提到的事情。它测试了1000万条记录,比CTE版本快300倍:)


    CTE
    version不是获取分层数据的最佳方式。()

    你应该做下面提到的事情。它测试了1000万条记录,比CTE版本快300倍:)


    您应该尽量避免使用标量函数。它们效率极低。更糟糕的是,当你开始往里面扔圈的时候。这听起来像是一个非常典型的递归cte。我不太清楚为什么要传递一个带分隔符的名称列表。@SeanRange-谢谢,我知道标量函数的效率低下-这只是我的一个要求。奇怪的要求,但不管怎样。如果你真的需要帮助,你能发布一些细节吗?这是一个递归查询,还是您只是试图解析一个带分隔符的列表并对每个值进行查询?这里最好是递归查询,但我不理解您的要求。您输入值
    “A/B/C/E/”
    ,但您将得到
    5
    。为什么是5?我想我现在明白了。你真的需要简单地传递“A”,而不是整个树。然后让您的递归cte计算出路径的其余部分。您应该尽量避免使用标量函数。它们效率极低。更糟糕的是,当你开始往里面扔圈的时候。这听起来像是一个非常典型的递归cte。我不太清楚为什么要传递一个带分隔符的名称列表。@SeanRange-谢谢,我知道标量函数的效率低下-这只是我的一个要求。奇怪的要求,但不管怎样。如果你真的需要帮助,你能发布一些细节吗?这是一个递归查询,还是您只是试图解析一个带分隔符的列表并对每个值进行查询?这里最好是递归查询,但我不理解您的要求。您输入值
    “A/B/C/E/”
    ,但您将得到
    5
    。为什么是5?我想我现在明白了。你真的需要简单地传递“A”,而不是整个树。然后让你的递归cte计算出路径的其余部分。比你快3分钟:-)谢谢你的提示!比你快3分钟:-)谢谢你的提示!
    Declare @table table(Id int, ParentId int, Name varchar(10))
    insert into @table values(1,NULL,'A')
    insert into @table values(2,1,'B')
    insert into @table values(3,2,'C')
    insert into @table values(4,1,'E')
    insert into @table values(5,3,'E')
    
    DECLARE @Counter tinyint = 0;
    
    IF OBJECT_ID('TEMPDB..#ITEM') IS NOT NULL
    DROP TABLE #ITEM
    
    CREATE TABLE #ITEM
    (
     ID int not null 
    ,ParentID int
    ,Name VARCHAR(MAX)
    ,lvl int not null
    ,RootID int not null
    )
    
    INSERT INTO #ITEM
        (ID,lvl,ParentID,Name,RootID)
    SELECT   Id
            ,0 AS LVL
            ,ParentId
            ,Name
            ,Id AS RootID
    FROM            
        @table
    WHERE
            ISNULL(ParentId,-1) = -1
    
    WHILE @@ROWCOUNT > 0
        BEGIN
            SET @Counter += 1
            insert into #ITEM(ID,ParentId,Name,lvl,RootID)
            SELECT  ci.ID
                    ,ci.ParentId
                    ,ci.Name
                    ,@Counter as cntr
                    ,ch.RootID
            FROM    
                @table AS ci
            INNER JOIN
                #ITEM AS pr 
            ON
                CI.ParentId=PR.ID
            LEFT OUTER JOIN
                #ITEM AS ch
            ON  ch.ID=pr.ID
            WHERE           
                    ISNULL(ci.ParentId, -1) > 0
                AND PR.lvl = @Counter - 1
    END
    
    select * from #ITEM