Как получить все листья родителя в иерархии типа спецификации - PullRequest
0 голосов
/ 03 мая 2019

У меня есть таблица спецификаций с:

ParentProdID, Qty, ComponentProdID

и два других, которые необходимо присоединить к таблице спецификации для условий, например:

ParentProdID = ProdTable.ProdID
ProdTable.TraitCode = SuppTable.TraitDesc

Я бы хотел иметь конечный стол с:

Parent code | Last component

где родительский код заполняет черты ProdID.A и Trait.B или Trait.C Последний компонент в дереве, который заполняет Trait.B (последний компонент может быть родительским, у которого есть дочерние элементы, но если ни один из дочерних элементов не имеет черты B, я хочу рассматривать их как листья)

Насколько я могу судить, SQL Server должен быть в состоянии сделать это или что-то подобное, но я довольно новичок в рекурсии с SQL. Большинство решений, которые я видел, используют рекурсивные CTE для иерархической печати всей спецификации в двух столбцах, что близко, но не так чисто, как хотелось бы.

Можно ли сделать признак материалом CTE и сослаться на него в (предположительно, CTE), который находит листья для родительского продукта?

Родители не должны быть настоящими корневыми узлами и могут быть чем угодно.

например. для спецификации:

Guitar(Wood, EndProduct)
-Neck(Wood, Neck)
--Strings(Metal, Neck)
--Hardware(Metal, Neck)
--NeckPiece(Wood, Neck)
-Body(Wood, Body)
--Bottom(Wood, Body)
--Top(Wood, Body)

будет напечатано:

Parent|Leaf
Guitar|NeckPiece
Neck|NeckPiece

Если такого рода вещи на SQL-сервере невозможны, распечатка списка всех дочерних элементов родителя вместе со счетчиком глубины, а затем упорядочение результирующего набора так, чтобы наивысшие оценки глубины находились наверху, получили бы функционально идентичную результат для функции поиска сверху вниз, такой как MATCH или VLOOKUP в Excel?

1 Ответ

0 голосов
/ 03 мая 2019

Впервые я использовал CTE, поэтому, вероятно, не оптимальное решение.Но достаточно показать, что это возможно в MSSQL.

Таблица part:

| id | parent_id | title                    |
| -- | --------- | ------------------------ |
|  1 |      NULL | Guitar(Wood, EndProduct) |
|  2 |         1 | Neck(Wood, Neck)         |
|  3 |         2 | Strings(Metal, Neck)     |
|  4 |         2 | Hardware(Metal, Neck)    |
|  5 |         2 | NeckPiece(Wood, Neck)    |
|  6 |         1 | Body(Wood, Body)         |
|  7 |         6 | Bottom(Wood, Body)       |
|  8 |         6 | Top(Wood, Body)          |

Запрос:

/*
* the CTE
*/
WITH parts_cte AS
(
    /* anchor:
    *  - part_level: the level in the hierarchy, with root starting at 0 not
    *                really necessary for this query, but still interesting
    *  - root_id:    NULL since this is the root and it has no root above it
    */
    SELECT
        p.id,
        p.parent_id,
        p.title,
        0 AS part_level,
        NULL as root_id
    FROM
        part p
    WHERE
        p.parent_id IS NULL
    /* recursion:
    *  - part_level: increased by one for each recursion step
    *  - root_id:    use the parent's root_id or fall back to parent's id
    */
    UNION ALL
    SELECT
        child.id,
        parent.id,
        child.title,
        parent.part_level + 1,
        ISNULL(parent.root_id, child.parent_id)
    FROM
        part child
        INNER JOIN parts_cte parent ON child.parent_id = parent.id
)

/*
* the actual statement that executes the CTE
*  1. get the parts to use as the parents
*  2. join the CTE as the leafs with matching root_id or matching parent_id
*  3. join the parts again on the leafs to see if any of the leafs have children
*     (which means they are not actually leafs)
*  4. remove lines without a leaf (to exclude the parts selected in 1. that are
*     actually leafs)
*  5. group by the column we want to show (to remove duplicates created by the 3.)
*  6. only keep lines where the leafs don't have any children of their own (see 3.)
*/
SELECT
    parent.id,
    parent.title AS parent,
    leaf.title AS leaf
FROM
    part parent
    LEFT OUTER JOIN parts_cte leaf ON
        parent.id = leaf.root_id
        OR parent.id = leaf.parent_id
    LEFT OUTER JOIN part leaf_child ON leaf.id = leaf_child.parent_id
WHERE
    leaf.id IS NOT NULL
GROUP BY
    parent.id,
    parent.title,
    leaf.title
HAVING
    COUNT(leaf_child.id) = 0
ORDER BY
    parent.id

Результат запроса:

| id | parent                   | leaf                  |
| -- | ------------------------ | --------------------- |
|  1 | Guitar(Wood, EndProduct) | Bottom(Wood, Body)    |
|  1 | Guitar(Wood, EndProduct) | Hardware(Metal, Neck) |
|  1 | Guitar(Wood, EndProduct) | NeckPiece(Wood, Neck) |
|  1 | Guitar(Wood, EndProduct) | Strings(Metal, Neck)  |
|  1 | Guitar(Wood, EndProduct) | Top(Wood, Body)       |
|  2 | Neck(Wood, Neck)         | Hardware(Metal, Neck) |
|  2 | Neck(Wood, Neck)         | NeckPiece(Wood, Neck) |
|  2 | Neck(Wood, Neck)         | Strings(Metal, Neck)  |
|  6 | Body(Wood, Body)         | Bottom(Wood, Body)    |
|  6 | Body(Wood, Body)         | Top(Wood, Body)       |
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...