SQL从自引用表获取父级

时间:2010-09-26 01:48:57

标签: c# sql sql-server linq

我有一张这样的表:

Item
{
   int ItemID
   int ParentID 
   string Name
}

Item实际上是较大的表Object的一个子集:

Object
{
    int ObjectID
    string Color
}

所以ItemIDObjectID的FK。在Item内,ParentID可以引用另一个项目或父对象。

我想要做的是能够从Item关系中的一个叶子一直迭代到它的父节点,直到我最终确定给定的Item叶子从哪个ObjectID下降。

我想在SQL中执行此操作。我正在使用SQL Server 2008。

这就是我的想法。我可以遍历项目ParentID,直到我无法再将ParentID加入另一个项目。这个ParentID是我要返回的ObjectID

我一直在尝试使用内连接来实现这一点,但没有运气。我正在使用C#所以如果这可以在linq中完成,我不怀疑它可以没有非常低效,那也没关系。

答案我去了:

WITH ObjectH (ParentID, ItemID, Level) AS
(
   -- Base case
   SELECT
      ParentID,
      ItemID,
      1 as Level 
   FROM Item
   WHERE ItemID = @param 

   UNION ALL

   -- Recursive step
   SELECT
      i.ParentID,
      i.ItemID,
      oh.Level + 1 AS Level
   FROM Item i
      INNER JOIN ObjectH oh ON
         c.ItemID = oh.ParentID           
)

SELECT TOP 1 ParentID
FROM ObjectH
ORDER BY Level DESC

这很有效。

3 个答案:

答案 0 :(得分:1)

我尝试使用递归SQL Server CTE执行此操作。

从逻辑上讲,这就是CTE的工作原理,但这应该是我从未使用过与你的模式完全相同的模式。

通常SQL中的树结构没有附加的父“Object”表。 (看起来你正在模仿你的对象在db中完全按1:1设计?如果我是你,我会重新审视那个模式)

尝试一下,让我知道它是否有效。

编辑:稍微更改了基本查询条件

EDIT2:更改了查询以使其特定于一个项目

WITH ObjectHierarchy (ItemID, Name, ParentID, Level) AS
(
   SELECT 
      ItemID, 
      Name, 
      ParentID, 
      1 as Level 
   FROM Item it, Object ob
   WHERE it.ParentID = ob.ObjectID
   AND ob.ItemID = @itemIdToBeSearched

   UNION ALL

   SELECT
      i.ItemID,
      i.Name,
      i.ParentID,
      oh.Level + 1 AS Level
   FROM Item i
   INNER JOIN ObjectHierarchy oh ON
      i.ParentID = oh.ItemID
   AND oh.ItemID = @itemIdToBeSearched

)

SELECT parentID
FROM ObjectHierarchy 
WHERE LEVEL = 1

答案 1 :(得分:1)

首先,允许ParentID列将两个不同的表作为“外键”引用是一种可怕的设计。您可能已经意识到这一点,因为您在编写此查询时遇到了困难。

话虽如此,假设您处于无法重新设计架构的位置,我会通过创建一个代表Item“应该”的视图来解决这个问题,然后使用传统的递归方法该视图上的CTE方法用于遍历层次结构。

create view vwItem 
as
select i.ItemID, 
       case when o.ObjectID is null then i.ParentID else null end as ParentID, 
       o.ObjectID, Name
    from Item i
        left join Object o
            on i.ParentID = o.ObjectID
go

;with cteItemList as (
    select i.ItemID, i.ParentID, i.ObjectID, i.Name, 1 as Level
        from vwItem i
        where i.ParentID is null
    union all
    select i.ItemID, i.ParentID, i.ObjectID, i.Name, il.Level+1 as Level
        from vwItem i
            inner join cteItemList il
                on i.ParentID = il.ItemID
)
select *
    from cteItemList
    order by Level, ItemID

答案 2 :(得分:0)

只需在SQL中创建一个为您迭代的标量函数,并将其包含在模仿您的表的视图中,但将该函数的结果添加为列。然后,您将LINQ对象基于视图,并在代码中具有ParentID。