SQL获取父记录链

时间:2016-03-12 20:35:57

标签: sql sql-server hierarchical-data recursive-query

我有一张这样的表:

|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个。

1 个答案:

答案 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;