我有一张这样的表:
|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 |
或者,甚至更好......也传递了一个父母的数量"并且取回那么多记录,因为可能有30个或更多父母,但我通常只想要8个。
答案 0 :(得分:3)
我是一个等级制度的传道者。 HierarchyID是SQL 2008中引入的一种不常用的数据类型,它编码一个层次结构。您拥有的数据是编码层次结构的经典方法。也就是说,每条记录都有一个(可能为空)父ID,指向表中的另一条记录。让我们一起来扩充您的数据。首先,bog标准递归公用表表达式(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;
请注意,我将其限制为返回层次结构中的两个级别,因为您的数据太浅而无法显示八个实际限制返回集。但是你需要做的就是在运行它时将@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;