Каскадное обновление первичного ключа для всех ссылающихся внешних ключей

11

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

# РЕДАКТИРОВАТЬ 1: Когда я запускаю запрос followinq

select * from sys.foreign_keys where referenced_object_id=OBJECT_ID('myTable') 

Я вижу, что update_referential_action имеет значение 0. Таким образом, после обновления столбцов моих первичных ключей не выполняется никаких действий. Как я могу обновить внешние ключи, чтобы сделать их ОБНОВЛЕНИЕМ КАСКАДА ?

# РЕДАКТИРОВАТЬ 2:
Для того, чтобы отсканировать создание или удаление всех внешних ключей в вашей схеме, запустите следующий скрипт (взято отсюда )

DECLARE @schema_name sysname;

DECLARE @table_name sysname;

DECLARE @constraint_name sysname;

DECLARE @constraint_object_id int;

DECLARE @referenced_object_name sysname;

DECLARE @is_disabled bit;

DECLARE @is_not_for_replication bit;

DECLARE @is_not_trusted bit;

DECLARE @delete_referential_action tinyint;

DECLARE @update_referential_action tinyint;

DECLARE @tsql nvarchar(4000);

DECLARE @tsql2 nvarchar(4000);

DECLARE @fkCol sysname;

DECLARE @pkCol sysname;

DECLARE @col1 bit;

DECLARE @action char(6);  

DECLARE @referenced_schema_name sysname;



DECLARE FKcursor CURSOR FOR

     select OBJECT_SCHEMA_NAME(parent_object_id)

         , OBJECT_NAME(parent_object_id), name, OBJECT_NAME(referenced_object_id)

         , object_id

         , is_disabled, is_not_for_replication, is_not_trusted

         , delete_referential_action, update_referential_action, OBJECT_SCHEMA_NAME(referenced_object_id)

    from sys.foreign_keys

    order by 1,2;

OPEN FKcursor;

FETCH NEXT FROM FKcursor INTO @schema_name, @table_name, @constraint_name

    , @referenced_object_name, @constraint_object_id

    , @is_disabled, @is_not_for_replication, @is_not_trusted

    , @delete_referential_action, @update_referential_action, @referenced_schema_name;

WHILE @@FETCH_STATUS = 0

BEGIN



      IF @action <> 'CREATE'

        SET @tsql = 'ALTER TABLE '

                  + QUOTENAME(@schema_name) + '.' + QUOTENAME(@table_name)

                  + ' DROP CONSTRAINT ' + QUOTENAME(@constraint_name) + ';';

    ELSE

        BEGIN

        SET @tsql = 'ALTER TABLE '

                  + QUOTENAME(@schema_name) + '.' + QUOTENAME(@table_name)

                  + CASE @is_not_trusted

                        WHEN 0 THEN ' WITH CHECK '

                        ELSE ' WITH NOCHECK '

                    END

                  + ' ADD CONSTRAINT ' + QUOTENAME(@constraint_name)

                  + ' FOREIGN KEY (';

        SET @tsql2 = '';

        DECLARE ColumnCursor CURSOR FOR

            select COL_NAME(fk.parent_object_id, fkc.parent_column_id)

                 , COL_NAME(fk.referenced_object_id, fkc.referenced_column_id)

            from sys.foreign_keys fk

            inner join sys.foreign_key_columns fkc

            on fk.object_id = fkc.constraint_object_id

            where fkc.constraint_object_id = @constraint_object_id

            order by fkc.constraint_column_id;

        OPEN ColumnCursor;

        SET @col1 = 1;

        FETCH NEXT FROM ColumnCursor INTO @fkCol, @pkCol;

        WHILE @@FETCH_STATUS = 0

        BEGIN

            IF (@col1 = 1)

                SET @col1 = 0;

            ELSE

            BEGIN

                SET @tsql = @tsql + ',';

                SET @tsql2 = @tsql2 + ',';

            END;

            SET @tsql = @tsql + QUOTENAME(@fkCol);

            SET @tsql2 = @tsql2 + QUOTENAME(@pkCol);

            FETCH NEXT FROM ColumnCursor INTO @fkCol, @pkCol;

        END;

        CLOSE ColumnCursor;

        DEALLOCATE ColumnCursor;

       SET @tsql = @tsql + ' ) REFERENCES ' + QUOTENAME(@referenced_schema_name) + '.' + QUOTENAME(@referenced_object_name)

                  + ' (' + @tsql2 + ')';

        SET @tsql = @tsql

                  + ' ON UPDATE ' + CASE @update_referential_action

                                        WHEN 0 THEN 'NO ACTION '

                                        WHEN 1 THEN 'CASCADE '

                                        WHEN 2 THEN 'SET NULL '

                                        ELSE 'SET DEFAULT '

                                    END

                  + ' ON DELETE ' + CASE @delete_referential_action

                                        WHEN 0 THEN 'NO ACTION '

                                        WHEN 1 THEN 'CASCADE '

                                        WHEN 2 THEN 'SET NULL '

                                        ELSE 'SET DEFAULT '

                                    END

                  + CASE @is_not_for_replication

                        WHEN 1 THEN ' NOT FOR REPLICATION '

                        ELSE ''

                    END

                  + ';';

        END;

    PRINT @tsql;

    IF @action = 'CREATE'

        BEGIN

        SET @tsql = 'ALTER TABLE '

                  + QUOTENAME(@schema_name) + '.' + QUOTENAME(@table_name)

                  + CASE @is_disabled

                        WHEN 0 THEN ' CHECK '

                        ELSE ' NOCHECK '

                    END

                  + 'CONSTRAINT ' + QUOTENAME(@constraint_name)

                  + ';';

        PRINT @tsql;

        END;

    FETCH NEXT FROM FKcursor INTO @schema_name, @table_name, @constraint_name

        , @referenced_object_name, @constraint_object_id

        , @is_disabled, @is_not_for_replication, @is_not_trusted

        , @delete_referential_action, @update_referential_action, @referenced_schema_name;

END;

CLOSE FKcursor;

DEALLOCATE FKcursor;  

Чтобы сгенерировать сценарий внешних ключей DROP, измените значение @action так, чтобы оно равнялось 'DROP' в условии объявления:

DECLARE @action char(6) = 'DROP';
mounaim
источник

Ответы:

9

Если вы определили ограничения Внешнего ключа как ON UPDATE CASCADEто, значение Первичного ключа, которое было изменено, должно каскадно распространяться на все Внешние ключи с этим ограничением.

Если у вас нет ON UPDATE CASCADEограничений, то вам потребуется создать сценарии для завершения обновления.

РЕДАКТИРОВАТЬ: Поскольку у вас нет ON UPDATE CASCADEограничения, но вы хотите, чтобы это настроить, это немного работы. SQL Server не поддерживает изменение ограничений на новый параметр.

Необходимо перебирать каждую таблицу, имеющую ограничение FK для таблицы PK. Для каждого стола с ФК:

  1. ALTER TABLE, чтобы удалить существующее ограничение FK.
  2. Еще раз ALTER TABLE для создания ограничения ON UPDATE CASCADE для рассматриваемого FK.

Это займет немного усилий, но приведет к тому, что ваше ограничение будет правильно установлено для вашего случая.

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

Сообщение от Джона Пола Кука можно найти здесь:

( http://social.technet.microsoft.com/wiki/contents/articles/2958.script-to-create-all-foreign-keys.aspx )

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

ДКП
источник
см. мою правку для получения дополнительной информации
mounaim
Вы знаете, как написать все внешние ключи @RLF?
Mounaim
@mounaim - Обновлено с примечанием о создании сценария.
RLF
Я работал над той же самой вещью, и та же самая ссылка видела мое редактирование @RLF
mounaim
1
здесь лучше включить блоки кода на DBA SE, потому что ссылки на другие сайты могут сломаться позже :)
mounaim
4

Вы уверены, что можете. ON UPDATE CASCADEэто то, что вы ищете.

Вот небольшая инструкция: http://sqlandme.com/2011/08/08/sql-server-how-to-cascade-updates-and-deletes-to-related-tables/

В основном, когда вы модифицируете PK, каскад выключится и обновит все FK, которые ссылаются на него. Это может быть сделано в вашем CREATEзаявлении, так же, как если бы вы делалиCASCADE DELETE

Следите за вещами, когда вы делаете это, потому что, насколько я понимаю, CASCADE фактически работает на уровне изоляции SERIALIZABLE(обычно SQL запускается READ COMMITTEDпо умолчанию) за кулисами, поэтому следите за любыми проблемами блокировки.

Дополнительную информацию об уровнях изоляции можно найти в этой статье: http://msdn.microsoft.com/en-us/library/ms173763.aspx

Крис Груттемейер
источник
3

Определите все внешние ключи как ОБНОВЛЕНИЕ КАСКАДА

Если вы этого не сделали, то вам придется

  1. Создать новую строку с новым первичным ключом
  2. Обновить все дочерние таблицы
  3. Удалить старый ряд

.. в транзакции, конечно, и остерегаясь других ограничений, которые могут потерпеть неудачу

ГБН
источник
спасибо @gbn. Можно ли обновить мои внешние ключи или мне просто нужно удалить их и воссоздать их с предложением ON CASCADE UPDATE?
mounaim
Оставь