CTE 对循环数据的意外结果

Unexpected result from CTE on cyclic data

我有这个table:

CREATE TABLE [dbo].[hierarchical] (
    [id]        INT           NOT NULL,
    [parent_id] INT           NULL,
    [name]      NVARCHAR (40) NULL
);

包含一些数据:

INSERT INTO [dbo].[hierarchical] ([id], [parent_id], [name]) VALUES (9, 11, N'43EB7203-3A7F-49A9-8C58-F18738D2BBC4')
INSERT INTO [dbo].[hierarchical] ([id], [parent_id], [name]) VALUES (10, 9, N'E202CAFA-4C0D-4A84-B02A-BF53AC3AFAB1')
INSERT INTO [dbo].[hierarchical] ([id], [parent_id], [name]) VALUES (11, 10, N'371C28E0-8C54-4A23-B28A-273979810E54')

如您所见,层次结构是循环的 9 => 11 => 10 => 9。

我正在尝试创建一个可以像这样处理这种循环数据的 CTE:

alter procedure GetHierarchicalTree
    @rootid int
as begin
    ;with computed as (
        select H.id, H.parent_id, 1 lvl, '[' + cast(H.id as nvarchar(max)) + ']' pat
        from hierarchical H
        where id = @rootid
        union all
        select H.id, H.parent_id, C.lvl + 1, C.pat + '[' + CAST(H.id as nvarchar(max)) + ']'
        from computed C
        inner join hierarchical H on C.id = H.parent_id
        where C.pat not like '%[' + cast(H.id as nvarchar(max)) + ']%'
    )
    select * from computed
    option (maxrecursion 0)
end

保护子句是 where C.pat not like '%[' + cast(H.id as nvarchar(max)) + ']%',因此如果 ID 已经包含,则停止。

但是当运行ID号为9的存储过程时:

exec gethierarchicaltree 9

sp return是 9 和 10,但不是 11,这很奇怪,因为 11 不应该在 computed.pat 中。

运行 11 returns 9 和 11,但不是 10。

更令人费解的是运行 on 10,因为sp只有returns 10.

为什么 sp 没有 return 9 和 11?

您的数据存在循环引用; 11 是 9 的父级,10 是 10 的父级,11 是 11 的父级,11 是 9 的父级,这...这不是层次结构,实际上推断某处存在设计缺陷。

怀疑你要的是:

CREATE PROCEDURE dbo.GetHierarchicalTree @rootid int
AS BEGIN
    WITH rCTE AS
        (SELECT H.id,
                H.parent_id,
                CONVERT(varchar(MAX), QUOTENAME(H.id)) AS [name]
         FROM dbo.hierarchical H
         WHERE H.id = @rootid
         UNION ALL
         SELECT H.id,
                H.parent_id,
                r.[name] + CONVERT(varchar(MAX), QUOTENAME(H.id))
         FROM rCTE r
              JOIN dbo.hierarchical H ON r.parent_id = H.id
                                     AND h.ID != @rootid)
    SELECT *
    FROM rCTE
    --OPTION (MAXRECURSION 0); --As you have a circular reference I strongly recommend this OPTION. You could very overwhelm your server.

END;

GO
EXEC dbo.GetHierarchicalTree 9;

我觉得你的问题出在LIKE,方括号[]不应该有:

where C.pat not like '%' + cast(H.id as nvarchar(max)) + '%'

更新:

我了解到您使用方括号来分隔您的路径。在那种情况下,您必须像这样转义左括号:

where C.pat not like '%[[]' + cast(H.id as nvarchar(max)) + ']%'

您还可以更改 where 条件以使用 charindex: CHARINDEX('[' + cast(H.id as nvarchar(max)) + ']',C.pat)=0