Как исправить рекурсивный CTE для установления зависимостей таблиц на основе sys.foreign_keys? - PullRequest
0 голосов
/ 25 апреля 2019

Рекурсивный CTE выдает дублированные результаты для вложенных зависимостей в формате иерархии. Я хочу иметь поле, в котором прописан путь к каждой опции.

Я пытался переписать это утверждение сверху вниз и снизу вверх, но я не смог устранить зависимости, которые на самом деле не существуют. Например, если у меня есть база A, ребенок B и внук C, я хочу A, A \ B и A \ B \ C - но не A \ C.

WITH rCTE
(
    [Level], -- Dimension count
    [RootSchema], -- Child table db schema
    [RootID], -- Child table ID, sys.foreign_keys.parent_object_ID
    [RTableName], -- Name of the child table
    [ParentSchema], -- Parent table db schema
    [ParentID], -- Parent table IDsys.foreign_keys.referenced_object_ID
    [PTableName], -- Name of the parent table
    [Path] -- The path to the item
) AS (
SELECT  
    1 as [Level],
    object_schema_name(f.parent_object_id),
    f.parent_object_id as [RootID],
    object_name(f.parent_object_id) as [RTableName],
    OBJECT_SCHEMA_NAME(f.referenced_object_ID),
    CONVERT(int,null) as [ParentID],
    object_name(referenced_object_id) as [PTableName],
    CONVERT(varchar(150),object_name(f.referenced_object_id) --+ '\' + ISNULL(object_name(f.parent_object_id),'') -- Troubleshooting
    ) as [Path]
FROM
    sys.foreign_keys f join sys.tables t on t.object_id = f.parent_object_id
    --WHERE NOT EXISTS 
    --( Select 1 
    --  from sys.foreign_keys ff 
    --  where f.parent_object_id = ff.referenced_object_id
    --)
UNION ALL
SELECT
    [Level]+1,
    object_schema_name(f.parent_object_id),
    f.parent_object_id,
    object_name(f.parent_object_id),
    OBJECT_SCHEMA_NAME(f.referenced_object_ID),
    f.referenced_object_id
    ,object_name(f.referenced_object_id)
    ,CAST(r.[Path] + '\' + r.[RTableName] as varchar(150))
from sys.foreign_keys f join rCTE r on f.referenced_object_id = r.rootID
    --where f.parent_object_id <> r.ParentID
)
select  distinct x.[level] -- change
        --,r.ParentSchema
        ,r.[PTableName]
        ,r.[RTableName]
        ,r.[Path]
from rCTE r join
    (
    select
        [ptableName], max([Level]) as [Level]
    from rCTE
    GROUP BY [pTableName]
    ) x on x.pTableName = r.pTableName
ORDER BY [Path]

--select distinct * from rcte

Я ссылался на многие сайты, но этот был лучшим, и это изображение, взятое оттуда, демонстрирует тот путь, о котором я говорю. Изображение примера кода с аналогичной целью

1 Ответ

0 голосов
/ 25 апреля 2019

Я обратился к Крису Венцелю из блога, на который я изначально ссылался в своем вопросе, после того, как получил письмо из его списка рассылки, в котором предлагалось отправить ему вызовы, с которыми вы сталкиваетесь. Он смог предоставить решение поставленного запроса, который я включил ниже. Это наметит восходящий подход к иерархиям, основанным исключительно на внешних ключах в базе данных.

;
WITH cte_FKtable(Parent

                       , FKConstraintName

                       , Child)

       AS (SELECT PO.name AS ParentTable

                      , FK.name

                      , RO.name AS ChildTable

             FROM sys.foreign_keys AS FK

                      INNER JOIN sys.objects AS RO ON RO.object_id = FK.referenced_object_id

                      INNER JOIN sys.objects AS PO ON PO.object_id = FK.parent_object_id),

       cte_allTable(parent

                           , FKConstraintName

                           , child)

       AS (SELECT Parent

                      , FKConstraintName

                      , Child

             FROM cte_FKtable

             UNION

             SELECT O.name

                      , NULL

                      , NULL

             FROM sys.objects AS O

             WHERE type = 'U' AND -- user table

                       NOT EXISTS

             (

                    SELECT 1

                    FROM cte_FKtable

                    WHERE( cte_FKtable.parent = o.name OR

                                  cte_FKtable.child = o.name

                             )

             )),

       cte_tree(name

                    , description

                    , level

                    , sort)

       AS (SELECT DISTINCT

                           parent

                      , CAST(parent AS varchar(1024))

                      , 1

                      , CAST(parent AS varchar(1024))

             FROM cte_allTable AS a

             WHERE a.parent NOT IN

             (

                    SELECT child

                    FROM cte_allTable

                    WHERE child IS NOT NULL

             ) OR

                       a.child IS NULL

             UNION ALL

             SELECT FK.child

                      , CAST(REPLICATE('|---', cte_tree.level) + FK.child AS varchar(1024))

                      , cte_tree.level + 1

                      , CAST(cte_tree.sort + '\' + FK.child AS varchar(1024))

             FROM cte_tree

                      INNER JOIN cte_FKtable AS FK ON cte_tree.name = FK.parent)

       SELECT DISTINCT

                    name

               , description

               , level

               , sort

       FROM cte_tree

       ORDER BY sort;
...