5

问题

我有一个递归 CTE 查询,但是在创建循环时它失败了。我已经修复了简单的循环(例如 1 -> 2 -> 1),但无法修复更复杂的循环(例如 1 -> 2 -> 3 -> 2)。

查询详情

测试表有两列:Base 和 Parent。我想要一个包含所有祖先的列表。

如果您从 test2 开始,我的查询适用于下面的示例数据,但不是从 test1 开始。

样本数据

Base    Parent
----    ------
test1   test2
test2   test3
test3   test2

SQL 查询(我尝试的修复在注释中标记)

;with sample_data (Base, Parent) as (
    select 'test1', 'test2'
    union select 'test2', 'test3'
    union select 'test3', 'test2'
),
nt_list (Base, Ancestor, [level]) as (
        select  Base,
                Parent Ancestor,
                1 [level]
        from    sample_data
        where   Base = 'test1' -- START HERE
        union all
        select  ntl.Base,
                nt.Parent,
                ntl.[level] + 1 [level]
        from    nt_list ntl
        join    sample_data nt on ntl.Ancestor = nt.Base
        where   nt.Parent <> ntl.Base -- fix recursive bug (e.g. 1 -> 2 -> 1)
        -- WHAT I TRIED TO ADD BUT CANNOT: (e.g. 1 -> 2 -> 3 -> 2)
        and     nt.Parent in (select Ancestor from nt_list)
)
select  distinct
        ntl.Base,
        ntl.Ancestor
from    nt_list ntl
order by Ancestor

SQL 错误:公用表表达式“nt_list”的递归成员具有多个递归引用。

4

4 回答 4

3

最终版本。假设'/'永远不会是基本名称或父名称的一部分。

;with sample_data (Base, Parent) as (
    -- TEST 1
    --        select 'test1', 'test2'
    --union   select 'test2', 'test3'
    --union   select 'test3', 'test2'
    -- TEST 2
            select 'test1', 'test2'
    union   select 'test2', 'test3'
    union   select 'test3', 'test4'
    union   select 'test3', 'test9'
    union   select 'test4', 'test5'
    union   select 'test5', 'test3'
    union   select 'test9', 'test8'
    -- TEST 3
    --        select 'test1', 'test2'
    --union   select 'test2', 'test3'
    --union   select 'test3', 'test1'
    -- TEST 4
    --        select  'test1', 'test1'
    --union   select  'test1', 'test2'
),
nt_list (Base, Ancestor, [level], [path]) as (
        select  Base,
                Parent Ancestor,
                1 [level],
                '/' + convert(varchar(max), rtrim(Base)) + '/' [path]
        from    sample_data
        where   Base = 'test1' -- START HERE
        union all
        select  ntl.Base,
                nt.Parent,
                ntl.[level] + 1 [level],
                ntl.[path] + rtrim(nt.Base) + '/'
        from    nt_list ntl
        join    sample_data nt on ntl.Ancestor = nt.Base
        where   ntl.path not like '%/' + rtrim(nt.Parent) + '/%'
)
select  distinct
        ntl.Base,
        ntl.Ancestor
from    nt_list ntl
order by Ancestor
于 2013-02-19T07:40:05.537 回答
2

您可以使用

;WITH nt_list (Base, Ancestor, [level], cycle, path)
     AS (SELECT Base,
                Parent                                                            Ancestor,
                1                                                                 [level],
                0                                                                 AS cycle,
                CAST('.' AS VARCHAR(max)) + ISNULL(Parent, '') + '.' + Base + '.' AS [path]
         FROM   NoteTest
         WHERE  Base = 'test1'
         UNION ALL
         SELECT ntl.Base,
                nt.Parent,
                ntl.[level] + 1                   [level],
                CASE
                  WHEN ntl.[path] LIKE '%.' + LTRIM(nt.Base) + '.%' THEN 1
                  ELSE 0
                END                               AS cycle,
                ntl.[path] + LTRIM(nt.Base) + '.' AS [path]
         FROM   nt_list ntl
                JOIN NoteTest nt
                  ON ntl.Ancestor = nt.Base
                     AND ntl.cycle = 0)
SELECT ntl.Base,
       ntl.Ancestor
FROM   nt_list ntl
ORDER  BY Ancestor 
于 2013-02-18T09:44:14.123 回答
0

我目前的解决方法是添加一个级别限制(添加and ntl.[level] <= 100到查询中)并让select distinct删除重复的条目。

于 2013-02-18T09:30:50.543 回答
0

这个案例是完美的,但就像你想要的更多项目一样:

    select 'test1', 'test2'
         union select 'test2', 'test3'
         union select 'test3', 'test4'
         union select 'test4', 'test5'
         union select 'test10', 'test11'
         union select 'test11', 'test30'
         ...

结果应该是:

    test1   test1 <- adding this
    test1   test2
    test1   test3
    test1   test4
    test1   test5
    test10  test10 <- adding this to multiple bases
    test10  test11
    test10  test30
于 2013-06-26T00:52:47.720 回答