8

我有 2 张桌子。 itemsitemItems

itemItems描述了 之间的多对多关系items。即一个成员items可以有很多孩子,他们可以有很多孩子,而这些孩子又可以有很多孩子等等。

物品:

itemID |  more stuff ......
1         ...    
2         ...
3         ...
4         ...

项目项目:

parentItemID |  childItemID  
1               2 
1               3
2               4

我想编写一个查询,该查询将递归地获取一个根节点下的所有子节点。

我相信这可以通过称为递归连接的方法实现,但我发现这个概念非常令人困惑......(类似于这个问题,但使用 sqlite 不是 sql server 并且多对多不是一对多)

我可以通过执行以下操作获得第一级(即一个项目下的所有孩子)

SELECT * 
FROM items 
INNER JOIN itemItems
ON items.itemID = itemItems.childItemID
WHERE itemItems.parentItemID = 1

我如何扩展它以递归地获取所有孩子的孩子等......?

4

2 回答 2

10

我刚刚得到了一个类似的查询来使用该with recursive语法。一般形式为:

with recursive tc( i )
  as ( select [... initial-query ...]
        union [... recursive-part (include tc) ...]
     )
 select * from tc;

在我的案例中,关键是确保 tc 列在递归部分中。此外,这个最终选择只是为了显示传递闭包的全部内容,真正的应该选择您需要的行。

我认为这个食谱将适用于你的情况,如下所示。我没有对此进行测试,我只是从我的查询中复制/粘贴并替换为您的表名。它确实对我有用,但我可能翻译错了。我也不太确定效率等,这只是我必须工作的事情。

with recursive tc( i )
  as ( select childItemID from itemItems where parentItemID = 1
        union select childItemID from itemItems, tc
               where itemItems.parentItemID = tc.i
     )
  select * from item where itemID in tc;

注意:这适用于我的版本 3.8.3.1,但不适用于 3.7.2。

于 2014-03-07T14:58:20.013 回答
1

上面的递归版本需要ANSI SQL:1999,当时通用DBMS都支持,但也有ANSI SQL-92方法可以实现有界递归。这种方法可以任意扩展。

下面的示例最多支持 7 个级别。如果您想要更多,请添加更多。

SELECT DISTINCT
    I.*
FROM
    item I INNER JOIN (
        SELECT 
            I1.itemID as Level1,
            I2.itemID as Level2,
            I3.itemID as Level3,
            I4.itemID as Level4,
            I5.itemID as Level5,
            I6.itemID as Level6,
            I7.itemID as Level7
        FROM 
            item I1 LEFT JOIN 
            item I2 ON EXISTS (SELECT NULL FROM itemItems II1 WHERE II1.parentItemID = I1.itemID AND I2.itemID = II1.childItemID) LEFT JOIN
            item I3 ON EXISTS (SELECT NULL FROM itemItems II2 WHERE II2.parentItemID = I2.itemID AND I3.itemID = II2.childItemID) LEFT JOIN
            item I4 ON EXISTS (SELECT NULL FROM itemItems II3 WHERE II3.parentItemID = I3.itemID AND I4.itemID = II3.childItemID) LEFT JOIN
            item I5 ON EXISTS (SELECT NULL FROM itemItems II4 WHERE II4.parentItemID = I4.itemID AND I5.itemID = II4.childItemID) LEFT JOIN
            item I6 ON EXISTS (SELECT NULL FROM itemItems II5 WHERE II5.parentItemID = I5.itemID AND I6.itemID = II5.childItemID) LEFT JOIN
            item I7 ON EXISTS (SELECT NULL FROM itemItems II6 WHERE II6.parentItemID = I6.itemID AND I7.itemID = II6.childItemID)
        WHERE
            I1.itemID = 1    -- root node condition
    ) D ON I.itemID = D.Level1 OR I.itemID = D.Level2 OR I.itemID = D.Level3 OR I.itemID = D.Level4 OR I.itemID = D.Level5 OR I.itemID = D.Level6 Or I.itemID = D.Level7
于 2013-05-03T05:51:34.477 回答