递归CTE;列出所有父母/祖父母的所有下层后代

时间:2020-04-15 03:19:52

标签: sql-server tsql recursion recursive-cte

我看到过一些类似问题的帖子,但是这些解决方案对我不起作用,因为我不想让中级父母作为孩子。我相当确定Recursive Common Table Expressions会成功。

基本上,我有一个列出所有父子关系的表,但是它只有1级深。我想查询该表,以便对于所有父项,返回所有底层的后代,以获得任意深度。例如:

 What the table looks like               what I'd like my query to return

     child | parent                             child | parent
     --------------                             --------------
       a1  |  b1                                  a2  |  b1
       a2  |  b1                                  a3  |  b1
       a3  |  b1                                  c1  |  b1
       c1  |  a1                                  c2  |  b1
       c2  |  a1                                  c1  |  a1
                                                  c2  |  a1

我一直在尝试的查询比我所看到的示例要复杂一些,因为我必须连接多个表才能使事情顺利进行。我必须将数据库ID与名称等结合起来。我也没有看到有人只要求像这样的底层后代。

我正在处理食谱数据库,因此这些“父母”是食谱,而“孩子”是库存物品。但是库存物料有可能成为子配方。由其他库存项目甚至可能由其他子配方组成。这就是这种任意深度的地方。我要编写的查询是各种“购物清单”-因此,对于存在的每个配方(配方或子配方),我想列出所有它的底层库存项目(即没有子配方,只有它们的单个组成部分)。幸运的是,我的一个表中有一列,其中有些位指示成分作为子食谱的状态。

到目前为止,这是我正在使用的工具,没有得到预期的结果:

RecpInv(“ rInv”)将父母与孩子相关联(项目ID与配方ID)

Inv('inv')将项目ID与项目名称相关

Recipe(“配方”)将配方ID与配方名称相关

WITH ingredients AS(
    SELECT 
        inv.ItemName AS 'Inventory Item',
        recipes.RecipeName AS 'Recipe',
        rInv.RecipeID AS 'Recipe ID',
        rInv.ItemID AS 'Item ID'

    FROM [DataDir].[dbo].[RecpInv] AS rInv
    INNER JOIN [DataDir].[dbo].[Inv] AS inv
        ON inv.ItemID = rInv.ItemID
    INNER JOIN [DataDir].[dbo].[Recipe] AS recipes
        ON recipes.RecipeID = rInv.RecipeID

    WHERE recipes.ProfCentID = @profitCenter
        AND inv.ProfCentID = @profitCenter

    UNION ALL

    SELECT 
        inv2.ItemName AS 'Inventory Item',
        recipes2.RecipeName AS 'Recipe',
        rInv2.RecipeID AS 'Recipe ID',
        rInv2.ItemID AS 'Item ID'

    FROM  [DataDir].[dbo].[RecpInv] AS rInv2
    INNER JOIN [DataDir].[dbo].[Recipe] AS recipes2
        ON rInv2.RecipeID = recipes2.RecipeID
    INNER JOIN [DataDir].[dbo].[Inv] AS inv2
        ON rInv2.ItemID = inv2.ItemID
    INNER JOIN ingredients
        ON rInv2.ItemID = ingredients.[Recipe ID]

    WHERE recipes2.ProfCentID = @profitCenter
        AND inv2.ProfCentID = @profitCenter
        AND inv2.SubRecipe = 0
)

SELECT
    ingredients.[Inventory Item],
    ingredients.[Recipe]

    FROM ingredients

    ORDER BY ingredients.[Recipe]

以下是一些查询结果。我从此查询中获得32,000行:

mayonnaise, vegan         Aioli, Roasted Garlic
Roasted Garlic Puree      Aioli, Roasted Garlic
salt, kosher              Aioli, Roasted Garlic
juice, lemon              Aioli, Roasted Garlic
mayonnaise, canola        Aioli, Truffle
spice, black truffle rub  Aioli, Truffle
pepper, black ground      Aioli, Truffle
mustard, stoneground      Aioli, Truffle
Garlic, Minced            Aioli, Truffle

这些是左侧食谱中正确的顶级配料。左栏中以大写字母开头的项目是子食谱,并且由库存项目和/或子食谱组成。我不希望在左边有任何子食谱。只是他们的底层组件。因此,“蒜泥蛋黄酱,烤大蒜”不应作为成分之一。应该包括“烤蒜泥”的底层成分。 “大蒜,切碎”也是如此。

我意识到这是一个很大的问题,但是可以提供任何帮助。

1 个答案:

答案 0 :(得分:0)

不确定这是否是最有效或雄辩的方法,但它给出了我一直在寻找的结果:

WITH ingredients AS
(
    SELECT 
        inv.ItemName AS 'Inventory Item',
        recipes.RecipeName AS 'Recipe',
        rInv.RecipeID AS 'Recipe ID',
        rInv.ItemID AS 'Item ID',
        inv.SubRecipe AS 'Sub'

    FROM [DataDir].[dbo].[RecpInv] AS rInv
    INNER JOIN [DataDir].[dbo].[Inv] AS inv
        ON inv.ItemID = rInv.ItemID
    INNER JOIN [DataDir].[dbo].[Recipe] AS recipes
        ON recipes.RecipeID = rInv.RecipeID

    WHERE recipes.ProfCentID = @profitCenter
        AND inv.ProfCentID = @profitCenter

    UNION ALL

    SELECT 
        inv2.ItemName AS 'Inventory Item',
        ingredients.[Recipe] AS 'Recipe',
        ingredients.[Recipe ID] AS 'Recipe ID',
        inv2.ItemID AS 'Item ID',
        inv2.SubRecipe AS 'Sub'

    FROM  [DataDir].[dbo].[RecpInv] AS rInv2
    INNER JOIN [DataDir].[dbo].[Recipe] AS recipes2
        ON rInv2.RecipeID = recipes2.RecipeID
    INNER JOIN [DataDir].[dbo].[Inv] AS inv2
        ON rInv2.ItemID = inv2.ItemID
    INNER JOIN ingredients
        ON rInv2.RecipeID = ingredients.[Item ID]

    WHERE recipes2.ProfCentID = @profitCenter
        AND inv2.ProfCentID = @profitCenter
)

SELECT DISTINCT
    ingredients.[Inventory Item],
    ingredients.[Recipe]

    FROM ingredients

    WHERE ingredients.[Sub] = 0

    ORDER BY ingredients.[Recipe], ingredients.[Inventory Item]