Курсор T-SQL + Динамический SQL (Количество строк, которые ссылаются на определенный первичный ключ) - PullRequest
0 голосов
/ 27 сентября 2019

Мне нужно посчитать, сколько раз ключ используется в разных таблицах.У меня есть следующий код, который получает все таблицы, которые ссылаются на ключ:

SELECT s.SCHEMA_NAME,
    OBJECT_NAME(f.parent_object_id) AS TableName,
    COL_NAME(fc.parent_object_id, fc.parent_column_id) AS ColumnName
FROM sys.foreign_keys AS f
    INNER JOIN sys.foreign_key_columns AS fc
        ON f.OBJECT_ID = fc.constraint_object_id
    OUTER APPLY
    (
        SELECT i.SCHEMA_NAME
        FROM INFORMATION_SCHEMA.SCHEMATA i
            INNER JOIN SYS.TABLES s
                ON i.SCHEMA_NAME = SCHEMA_NAME(s.SCHEMA_ID)
        WHERE f.parent_object_id = s.object_id
    ) AS s
WHERE OBJECT_NAME (f.referenced_object_id) = 'Languages'

Результат:

SchemaName, TableName, ColumnName

Ивот запрос, который я должен сделать для каждой возвращаемой строки, и, наконец, СУММ всех из них:

SELECT COUNT(*)
FROM SchemaName.TableName t
WHERE t.ColumnName = @LanguageId

Я читал о CURSOR и динамическом SQL, но я не смог найти способ сделать эторабота (никогда не использовал ни один из них).

Использование курсоров / динамического SQL не является обязательным.Если есть более простые способы, которые я ценю.

РЕДАКТИРОВАТЬ: мне удалось заставить его работать.РЕДАКТИРОВАТЬ 2: Некоторый рефакторинг и полная реализация фактического требования.

DECLARE @WantedDefaultLanguageId INT = 1;

--Internal Variables
DECLARE @DefaultLanguageId INT = (SELECT Id FROM i18n.Languages WHERE IsDefault = 1)
    , @SqlCommand NVARCHAR(1000)
    , @SchemaName SYSNAME
    , @TableName SYSNAME
    , @FieldName SYSNAME
    , @CurrentValue INT
    , @DefaultTotal INT = 0
    , @WantedTotal INT = 0;

DECLARE relationships CURSOR
    LOCAL FAST_FORWARD READ_ONLY 
    FOR SELECT schemaNames.SCHEMA_NAME,
            OBJECT_NAME(foreignKeys.parent_object_id) AS TableName,
            COL_NAME(foreignKeysColumns.parent_object_id, foreignKeysColumns.parent_column_id) AS ColumnName
        FROM sys.foreign_keys AS foreignKeys
            INNER JOIN sys.foreign_key_columns AS foreignKeysColumns
                ON foreignKeys.OBJECT_ID = foreignKeysColumns.constraint_object_id
            OUTER APPLY
            (
                SELECT metadata.SCHEMA_NAME
                FROM INFORMATION_SCHEMA.SCHEMATA metadata
                    INNER JOIN SYS.TABLES AS sysTables
                        ON metadata.SCHEMA_NAME = SCHEMA_NAME(sysTables.SCHEMA_ID)
                WHERE foreignKeys.parent_object_id = sysTables.object_id
            ) AS schemaNames
        WHERE OBJECT_NAME (foreignKeys.referenced_object_id) = 'Languages';

IF @DefaultLanguageId = @WantedDefaultLanguageId
    SELECT 1;
ELSE
    BEGIN
        OPEN relationships
            FETCH NEXT FROM relationships
            INTO @SchemaName, @TableName, @FieldName; 

            WHILE @@FETCH_STATUS = 0  
            BEGIN   
                SET @SqlCommand = '
                    SELECT @CurrentValue = COUNT(*)
                    FROM ' + @SchemaName + '.' + @TableName + ' tableName
                    WHERE tableName.' + @FieldName + ' = ' + CAST(@DefaultLanguageId AS nvarchar(1000))

                EXEC sp_executesql @SqlCommand, N'@CurrentValue INT OUTPUT', @CurrentValue OUTPUT

                SET @DefaultTotal += @CurrentValue

                --■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

                SET @SqlCommand = '
                    SELECT @CurrentValue = COUNT(*)
                    FROM ' + @SchemaName + '.' + @TableName + ' tableName
                    WHERE tableName.' + @FieldName + ' = ' + CAST(@WantedDefaultLanguageId AS nvarchar(1000))

                EXEC sp_executesql @SqlCommand, N'@CurrentValue INT OUTPUT', @CurrentValue OUTPUT

                SET @WantedTotal += @CurrentValue

                --■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

                FETCH NEXT FROM relationships
                INTO @SchemaName, @TableName, @FieldName; 
            END 

        CLOSE relationships
        DEALLOCATE relationships

        SELECT CASE 
            WHEN @WantedTotal = @DefaultTotal THEN
                1
            ELSE 0
        END
    END;

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

Edit3: Хорошо, вот пример того, что мне нужно:

Table: Language
Id     Language
1      English

RelationalTable1
... LanguageId
    1
    1
    2

AllOtherRelationalTables
...

Мне нужно сделать COUNT, сколько разLanguageId = 1 (для всех таблиц, которые ссылаются на таблицу языков).Код работает, но хотел бы знать, есть ли более простой способ реализовать / улучшить его.

1 Ответ

0 голосов
/ 27 сентября 2019

Я не совсем уверен, что именно вы запрашиваете, но это МОЖЕТ вам помочь ...

Этот SQL получит вам всю информацию о столбце FK:

SELECT fkeys.[name] AS FKName, 
        OBJECT_NAME(fkeys.parent_object_id) AS TableName,
        (SELECT STUFF((SELECT ',' + c.[name]
         FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.parent_object_id = t.object_id
              INNER JOIN sys.columns as c ON t.object_id = c.object_id
              INNER JOIN sys.foreign_key_columns AS fc ON c.column_id = fc.parent_column_id 
                                                      AND fc.constraint_object_id = fk.object_id 
                                                      AND fc.parent_object_id = fk.parent_object_id 
        WHERE fk.[name] = fkeys.[name]
        FOR XML PATH ('')), 1, 1, '')) AS FKFolumns,
        OBJECT_NAME(fkeys.referenced_object_id) AS ReferencedTableName,
        (SELECT STUFF((SELECT ',' + c.[name]
         FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.referenced_object_id = t.object_id
              INNER JOIN sys.columns as c ON t.object_id = c.object_id
              INNER JOIN sys.foreign_key_columns AS fc ON c.column_id = fc.referenced_column_id 
                                                      AND fc.constraint_object_id = fk.object_id 
                                                      AND fc.referenced_object_id = fk.referenced_object_id 
        WHERE fk.[name] = fkeys.[name]
        FOR XML PATH ('')), 1, 1, '')) AS ReferencedFKColumns
   FROM sys.foreign_keys fkeys

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

WITH AllFKInfo AS (
    SELECT fkeys.[name] AS FKName, 
            OBJECT_NAME(fkeys.parent_object_id) AS TableName,
            (SELECT STUFF((SELECT ',' + c.[name]
             FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.parent_object_id = t.object_id
                  INNER JOIN sys.columns as c ON t.object_id = c.object_id
                  INNER JOIN sys.foreign_key_columns AS fc ON c.column_id = fc.parent_column_id 
                                                          AND fc.constraint_object_id = fk.object_id 
                                                          AND fc.parent_object_id = fk.parent_object_id 
            WHERE fk.[name] = fkeys.[name]
            FOR XML PATH ('')), 1, 1, '')) AS FKFolumns,
            OBJECT_NAME(fkeys.referenced_object_id) AS ReferencedTableName,
            (SELECT STUFF((SELECT ',' + c.[name]
             FROM sys.foreign_keys fk INNER JOIN sys.tables t ON fk.referenced_object_id = t.object_id
                  INNER JOIN sys.columns as c ON t.object_id = c.object_id
                  INNER JOIN sys.foreign_key_columns AS fc ON c.column_id = fc.referenced_column_id 
                                                          AND fc.constraint_object_id = fk.object_id 
                                                          AND fc.referenced_object_id = fk.referenced_object_id 
            WHERE fk.[name] = fkeys.[name]
            FOR XML PATH ('')), 1, 1, '')) AS ReferencedFKColumns
       FROM sys.foreign_keys fkeys
)
SELECT ReferencedTableName, ReferencedFKColumns, COUNT(ReferencedFKColumns) AS CountOfReferences
FROM AllFKInfo
GROUP BY ReferencedTableName, ReferencedFKColumns
ORDER BY ReferencedTableName, ReferencedFKColumns 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...