【问题标题】:Simple CTE recursive query简单的 CTE 递归查询
【发布时间】:2018-02-28 15:12:48
【问题描述】:

很抱歉打扰这么简单的问题,但我决定学习 CTE 递归查询,即使在确定了许多源和线程的范围后,我也无法完成我的查询工作。所以我谦虚地要求指出我的错误。

这是我正在查询的表的一部分:

ID        ContainerInstanceID  ItemID      ContentContainerInstanceID
--------- -------------------- ----------- --------------------------
73        40                   NULL        41        
69        40                   23885       NULL
68        40                   29683       NULL
67        40                   29686       NULL
72        41                   27392       NULL
71        41                   29235       NULL
70        41                   29213       NULL

我组装了这个简单的 CTE 查询:

;WITH ContainerContent_CTE(InstanceID,ItemID,ContentContainerInstanceID) AS
 (
  -- ROOT set accordig to input parameter
  SELECT ContainerInstanceID,SCA.ItemID,SCA.ContentContainerInstanceID 
  FROM StockContainerAssignments as SCA 
  WHERE SCA.ContainerInstanceID = 40  -- input parameter

  UNION ALL

  -- recursive data
  SELECT ContainerInstanceID,SCA2.ItemID,SCA2.ContentContainerInstanceID 
  FROM ContainerContent_CTE AS CC
  JOIN StockContainerAssignments as SCA2 on CC.InstanceID = SCA2.ContentContainerInstanceID
  )
SELECT * FROM ContainerContent_CTE;

我要做的是获取一个*容器,在本例中它的 ID = 40,这是我的输入参数。然后,我尝试通过将 ContainerInstanceID 与 ContentContainerInstanceID 链接来连接其他级别。在我的示例中,它不是空 ar row ID = 73。这应该会在我的结果集中再添加 3 行(因此它应该看起来类似于我上面提供的示例数据),但我仍然只得到*行:

InstanceID   ItemID  ContentContainerInstanceID
-----------  ----------- --------------------------
40           29686       NULL
40           29683       NULL
40           23885       NULL
40           NULL        41        

感谢提示,以帮助我偶然发现这个主题。

【问题讨论】:

  • 你的 FROM 和 JOIN 表在下半场倒退了。
  • 您使用的是哪个版本?您尝试过交叉连接而不是连接吗?
  • @Sean,我都试过了(我在网上看到了这两个例子),实际上这是第二个版本。它对结果集没有任何影响......
  • 而且由于您只是以 ctes 开头,因此它们实际上并不以分号开头。 cte 的要求是前一个语句必须以一个结尾。如此多的例子表明它们是这样的。这只是我的一个小烦恼。分号是语句终止符,而不是语句开始符。 :)
  • @Sean Lange:是​​的,我习惯使用终结器,我过去使用过 CTE,但在大多数情况下都采用了其他人的解决方案。在这种情况下,这就是我习惯“初学者”的地方:-)

标签: sql sql-server common-table-expression recursive-query


【解决方案1】:

你只是有一些小事情不合适。这应该适合你。

with ContainerContent_CTE as
(
    select SCA.ContainerInstanceID
        ,SCA.ItemID
        ,SCA.ContentContainerInstanceID 
    FROM StockContainerAssignments as SCA 
    WHERE SCA.ContainerInstanceID = 40  -- input parameter

    UNION ALL 

    select SCA.ContainerInstanceID
        ,SCA.ItemID
        ,SCA.ContentContainerInstanceID 
    FROM StockContainerAssignments as SCA
    inner join ContainerContent_CTE cte on cte.ContentContainerInstanceID = SCA.ContainerInstanceID
)

select *
from ContainerContent_CTE

【讨论】:

  • 是的,它确实有效。我将其标记为答案,因为为了清楚起见,您保留了命名约定。
【解决方案2】:

这对我有用

declare @t table (id int, instance int, container int);
insert into @t values 
               (73, 40, 41)       
             , (69, 40, NULL)
             , (68, 40, NULL)
             , (67, 40, NULL)
             , (72, 41, NULL)
             , (71, 41, NULL)
             , (70, 41, NULL);
select * from @t;
with cte as 
( select t.id, t.instance, t.container
  from @t t
  where t.instance = 40 
  union all 
  select t.id, t.instance, t.container
  from cte 
  join @t t 
    on t.instance = cte.container
)
select * from cte;

【讨论】:

  • 经过测试,这也有效。我只能标记一个答案,所以我只对这个答案投了赞成票。谢谢@Paparazzi。
  • @Oak_3260548 我没有得到复选标记很好。最初的命名对您来说更清楚,但对于寻求类似解决方案的其他人来说却不是更清楚。
【解决方案3】:

正如预期的那样,在某种程度上出现了一个愚蠢的小错误 - 在 ON 子句中,我将父母和孩子与相反的 ID 对联系起来。因为我只是在学习 CTE,所以对我来说很难看到。这里是固定的(供参考):

;WITH ContainerContent_CTE(InstanceID,temID,ContentContainerInstanceID) AS
 (
  -- ROOT set accordig to input parameter
  SELECT ContainerInstanceID,SCA.ItemID,SCA.ContentContainerInstanceID 
  FROM StockContainerAssignments as SCA 
  WHERE SCA.ContainerInstanceID = 40  -- input parameter

  UNION ALL

  -- recursive data
  SELECT ContainerInstanceID,SCA2.ItemID,SCA2.ContentContainerInstanceID 
  FROM ContainerContent_CTE AS CC
  INNER JOIN StockContainerAssignments as SCA2 on CC.ContentContainerInstanceID = SCA2.ContainerInstanceID)
SELECT * FROM ContainerContent_CTE;

感谢您的建议。

【讨论】: