SQL获取Parent记录链

SQL Get Parent Record Chain

我有一个 table 像这样:

|ItemID|ItemName|ParentID|
|... etc more records ...|
|1234  |Itemlol |432     |
|... etc more records ...|
|1543  |Kthxhi  |1234    |
|... etc more records ...|
|1938  |FunTimes|1543    |
|... etc more records ...|
|3765  |Apples  |1938    |
|... etc more records ...|
|8634  |Oranges |3765    |
|... etc more records ...|

我可以在输入 3765 的地方进行查询并返回吗:

|1234  |Itemlol |432     |
|1543  |Kthxhi  |1234    |
|1938  |FunTimes|1543    |

或者,甚至更好...还传入一个 "Number of Parents" 并返回那么多记录,因为可能有 30 条或更多条 parents,但我通常只需要 8 条。

我是等级制度的布道者。 HierarchyID 是 SQL 2008 年引入的一种不常用的数据类型,它编码层次结构。您拥有的数据是对层次结构进行编码的经典方法。也就是说,每条记录都有一个(可能为空)父 ID,指向 table 中的另一条记录。让我们逐步了解如何扩充您的数据。首先,一个沼泽标准递归通用table表达式(CTE):

create table #items (
    ItemID int not null,
        constraint PK_Items primary key clustered (ItemID),
    Name nvarchar(100) not null,
    ParentItemID int null,
        constraint [FK_Items_Parent] foreign key
            (ParentItemID) references #items (ItemID)
);

insert into #items (ItemID, Name, ParentItemID)
values
    (1234  ,'Itemlol ', null     ),
    (1543  ,'Kthxhi  ', 1234    ),
    (1938  ,'FunTimes', 1543    ),
    (3765  ,'Apples  ', 1938    ),
    (8634  ,'Oranges ', 3765    );

alter table #items add h hierarchyid null;

with cte as (
    select ItemID, Name, ParentItemID, cast(concat('/', ItemID, '/') as varchar(1000)) as h
    from #items
    where ParentItemID is null

    union all

    select child.ItemID, child.Name, child.ParentItemID, cast(concat(parent.h, child.ItemID, '/') as varchar(1000)) as h
    from #items as child
    join cte as parent
        on child.ParentItemID = parent.ItemID
)
update i
    set h = cte.h
from #items as i
join cte
    on i.ItemID = cte.ItemID;

请注意,我稍微更改了您的数据,以便有一个明确的递归锚点。我们在这里所做的是计算每一行的 hierarchyid 列应该具有的值。接下来,回答你的实际问题:

declare @h hierarchyid, @maxLevels tinyint = 2;
set @h = (select h from #items where ItemID = 3765);

select *
from #items as i
where @h.IsDescendantOf(i.h) = 1
    and i.h.GetLevel() >= @h.GetLevel() - @maxLevels
    and i.h <> @h;

请注意,我将其限制在被 return 编辑的层次结构中向上两个级别,因为您的数据太浅,无法显示实际上限制 return 集的八个级别。但是您需要做的就是在 运行 时将 @maxLevels 的值更改为 8,这样您就可以开始了。

编辑:由于您对经典的 CTE 方法表示了兴趣,这里有一种方法可以实现。本质上,您是在从子级到父级而不是从父级到子级遍历层次结构。不过,请根据您的实际数据在性能方面比较这两个解决方案!

declare @maxLevel tinyint = 2, @ItemID int = 3765;
with cte as (

    select *, 0 as [level]
    from #items
    where ItemID = @ItemID

    union all

    select parent.*, child.level + 1 as [level]
    from #items as parent
    inner join cte as child
        on child.ParentItemID = parent.ItemID
)
select *
from cte
where [level] <= @maxLevel
    and ItemID <> @ItemID;