Рекурсивный CTE; Список всех потомков нижнего уровня для всех родителей / бабушек и дедушек - PullRequest
1 голос
/ 15 апреля 2020

Я видел несколько сообщений о похожих проблемах, но решения для меня не работают, так как я не хочу показывать родителей среднего звена детьми. Я вполне уверен, что 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

Запрос, который я пробовал, немного сложнее, чем примеры, которые я видел, потому что мне нужно объединить несколько таблиц, чтобы все заработало. Я должен объединить идентификаторы базы данных с именами, et c. Я также не видел, чтобы кто-то спрашивал только о потомках нижнего уровня, подобных этому.

Я имею дело с базой данных рецептов, поэтому эти "родители" - это рецепты, а "дети" - предметы инвентаря. Но предмет инвентаря может быть суб-рецептом; состоит из других предметов инвентаря и, возможно, даже других суб-рецептов. Вот где возникает этот произвольный уровень глубины. Я пытаюсь написать запрос типа «список покупок», поэтому для каждого существующего рецепта (рецепт или подрецепт) я бы хотел перечислить все его предметы инвентаря нижнего уровня (то есть никакие подрецепты, только их отдельные компоненты). К счастью, в одной из моих таблиц есть столбец с битом, указывающим статус ингредиента как подрецепт.

Вот то, с чем я работаю до сих пор, но это не дает желаемого результата:

Таблица RecpInv ('rInv') связывает родителей с детьми (идентификатор элемента с идентификатором рецепта)

Таблица Inv ('inv') связывает идентификатор элемента с именем элемента

Таблица Recipe ('recipes') связывает идентификатор рецепта с именем рецепта

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

Это правильные ингредиенты верхнего уровня слева для рецептов справа. Предметы, начинающиеся с заглавной буквы в левом столбце, являются субрецептами и состоят из инвентарных предметов и / или субрецептов. Я не хочу никаких рецептов слева. Просто их компоненты нижнего уровня. Таким образом, в айоли, жареном чесноке c, не должно быть пюре из жареного чеснока c в качестве одного из ингредиентов. Вместо этого должны быть включены компоненты нижнего уровня для 'Roasted Garli c Puree'. То же самое касается «Гарли c, фарш».

Я понимаю, что это довольно большая просьба, но любая помощь приветствуется.

1 Ответ

0 голосов
/ 15 апреля 2020

Не уверен, что это самый эффективный или красноречивый способ сделать это, но он дал результат, который я искал:

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]
...