Published on

June 28, 2011

Пересмотр сценария SQL Server

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

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

После некоторых экспериментов я нашел решение, которое включает использование Object_ID для более эффективной обработки циклических ссылок. В якорной части рекурсивного CTE я объединяю Object_ID с другими полями, чтобы создать уникальный идентификатор. Это помогает устранить циклические ссылки и позволяет сценарию работать по назначению.

Еще одно улучшение, которое я внес в сценарий, – это удаление ненужных соединений с sys.columns. Вместо этого я использую функцию COL_NAME() для получения имен столбцов родительского и дочернего объектов. Это изменение не только упрощает сценарий, но и обеспечивает небольшой прирост производительности.

Наконец, я решил переименовать CTE в сценарии, чтобы они были более понятными. Новые имена, “Hierarchy” и “Ancestry”, лучше отражают их назначение и делают сценарий более понятным.

Без лишних слов, вот пересмотренная версия сценария:


DECLARE @StartParentTable VARCHAR(256)
SELECT @StartParentTable = 'yourtable'

;WITH Hierarchy (ParentTable, ParentColumn, ChildTable, ChildColumn, FKLevel, SortCol) AS (
    SELECT OBJECT_NAME(sfk.referenced_object_id) AS ParentTable
        ,COL_NAME(sfkc.referenced_object_id, sfkc.referenced_column_id) AS ParentColumn
        ,OBJECT_NAME(sfk.Parent_object_id) AS ChildTable
        ,COL_NAME(sfkc.parent_object_id, sfkc.parent_column_id) AS ChildColumn
        ,0 AS FKLevel
        ,CAST('.' + CAST(OBJECT_NAME(sfk.referenced_object_id) AS VARCHAR(MAX)) + '.' AS VARCHAR(MAX))
    FROM sys.foreign_key_columns sfkc
    INNER JOIN sys.foreign_keys sfk ON sfkc.constraint_object_id = sfk.object_id
    WHERE OBJECT_NAME(sfk.referenced_object_id) = @StartParentTable

    UNION ALL

    SELECT OBJECT_NAME(sfk.referenced_object_id) AS ParentTable
        ,COL_NAME(sfkc.referenced_object_id, sfkc.referenced_column_id) AS ParentColumn
        ,OBJECT_NAME(sfk.Parent_object_id) AS ChildTable
        ,COL_NAME(sfkc.parent_object_id, sfkc.parent_column_id) AS ChildColumn
        ,FKLevel + 1
        ,STUFF(('.' + F.SortCol + CAST(CAST(OBJECT_NAME(sfk.referenced_object_id) AS VARCHAR(MAX)) + '.' AS VARCHAR(MAX))), 1, 1, '')
    FROM sys.foreign_keys sfk
    INNER JOIN Hierarchy F ON OBJECT_NAME(sfk.referenced_object_id) = f.ChildTable
        AND F.SortCol NOT LIKE '%' + CAST(OBJECT_NAME(sfk.referenced_object_id) AS VARCHAR(MAX)) + '%'
    INNER JOIN sys.foreign_key_columns sfkc ON sfkc.constraint_object_id = sfk.object_id
    WHERE OBJECT_NAME(sfk.referenced_object_id) <> @StartParentTable
        AND sfk.referenced_object_id <> sfk.parent_object_id
), Ancestry (ParentTable, ParentColumn, ChildTable, ChildColumn, FKLevel, SortCol) AS (
    SELECT DISTINCT OBJECT_NAME(sfk.referenced_object_id) AS ParentTable
        ,COL_NAME(sfkc.referenced_object_id, sfkc.referenced_column_id) AS ParentColumn
        ,OBJECT_NAME(sfk.Parent_object_id) AS ChildTable
        ,COL_NAME(sfkc.parent_object_id, sfkc.parent_column_id) AS ChildColumn
        ,-1 AS FKLevel
        ,CAST('.' + CAST(OBJECT_NAME(sfk.referenced_object_id) AS VARCHAR(MAX)) + '.' AS VARCHAR(MAX))
    FROM Hierarchy F
    INNER JOIN sys.foreign_keys sfk ON f.ChildTable = OBJECT_NAME(sfk.parent_object_id)
        AND F.ParentTable <> OBJECT_NAME(sfk.referenced_object_id)
    INNER JOIN sys.foreign_key_columns sfkc ON sfkc.constraint_object_id = sfk.object_id

    UNION ALL

    SELECT OBJECT_NAME(sfk.referenced_object_id) AS ParentTable
        ,COL_NAME(sfkc.referenced_object_id, sfkc.referenced_column_id) AS ParentColumn
        ,OBJECT_NAME(sfk.Parent_object_id) AS ChildTable
        ,COL_NAME(sfkc.parent_object_id, sfkc.parent_column_id) AS ChildColumn
        ,f.FKLevel - 1
        ,STUFF(('.' + SortCol + CAST(CAST(OBJECT_NAME(sfk.referenced_object_id) AS VARCHAR(MAX)) + '.' AS VARCHAR(MAX))), 1, 1, '')
    FROM Ancestry F
    INNER JOIN sys.foreign_keys sfk ON f.parentTable = OBJECT_NAME(sfk.parent_object_id)
    INNER JOIN sys.foreign_key_columns sfkc ON sfkc.constraint_object_id = sfk.object_id
    WHERE f.parentTable NOT IN (SELECT ParentTable FROM Hierarchy)
        AND sfk.referenced_object_id <> sfk.parent_object_id
        AND f.ChildTable NOT IN (SELECT ChildTable FROM Hierarchy)
)
SELECT ParentTable, ParentColumn, ChildTable, ChildColumn, FKLevel, SortCol
FROM Hierarchy

UNION ALL

SELECT ParentTable, ParentColumn, ChildTable, ChildColumn, FKLevel, SortCol
FROM Ancestry
ORDER BY SortCol ASC
OPTION (MAXRECURSION 500)

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

Спасибо за чтение!

Click to rate this post!
[Total: 0 Average: 0]

Let's work together

Send us a message or book free introductory meeting with us using button below.