Каскадне оновлення первинного ключа для всіх посилань на зовнішні ключі


11

Чи можливо оновити значення стовпця первинного ключа за допомогою каскадування оновлення серед усіх зовнішніх ключів, на які посилається на нього?

# EDIT 1: Коли я запускаю наступний запит

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

, Я бачу, що оновлення update_referential встановлено на 0. Отже, після оновлення стовпців моїх первинних ключів НЕ ДІЙСЯ. Як я можу оновити зовнішні ключі, щоб зробити їх НА КАСКАДНОМУ ОНОВЛЕННІ ?

# EDIT 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';

Відповіді:


9

Якщо ви визначили обмеження зовнішнього ключа, ON UPDATE CASCADEто значення Первинного ключа, яке було змінено, повинно спускатися до всіх зовнішніх ключів з цим обмеженням.

Якщо у вас немає ON UPDATE CASCADEобмежень, вам знадобиться створити сценарії для завершення оновлення.

EDIT: Оскільки у вас немає ON UPDATE CASCADEобмежень, але ви хочете, щоб це було налаштовано, це трохи працювати. SQL Server не підтримує зміни обмежень на нове налаштування.

Потрібно перебрати кожну таблицю, яка має обмеження FK до таблиці PK. Для кожної таблиці з FK:

  1. ALTER TABLE, щоб скинути існуюче обмеження FK.
  2. АЛЬТЕР ТАБЛИЦА знову, щоб створити обмеження НА ОНОВЛЕННЯ КАСКАДУ для відповідного ФК.

Це вимагає трохи зусиль, але це призведе до того, що ваше обмеження буде належним чином встановлено для вашої справи.

EDIT 2: Інформація, яка вам потрібна, знаходиться в sys.foreign_keys. Ви можете вибрати з цієї таблиці, щоб отримати всю необхідну інформацію.

Пост від Джона Пола Кука можна знайти тут:

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

Цей код знизиться та створить ВСІ обмеження FK у базі даних. Ви повинні мати можливість працювати над цим, щоб вносити в базу даних лише ті зміни, які ви хочете.


дивіться мою
редакцію

Чи знаєте ви, як скриптувати всі зовнішні ключі @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-delete-to-related-tables/

В основному, коли ви модифікуєте ПК, каскад вимкнеться та оновить усі FK-файли, на які посилається. Це можна зробити у вашій CREATEзаяві так само, як якщо б ви робилиCASCADE DELETE

Слідкуйте за речами, коли ви це робите, тому що, наскільки я розумію, CASCADE насправді працює на рівні ізоляції SERIALIZABLE(як правило, SQL працює READ COMMITTEDза замовчуванням) позаду кулісів, тому слідкуйте за будь-якими проблемами блокування.

Додаткову інформацію про рівні ізоляції можна знайти в цій статті: http://msdn.microsoft.com/en-us/library/ms173763.aspx


3

Визначте всі закордонні ключі як ОНОВЛЕННЯ CASCADE

Якщо ви цього не зробили, тоді вам доведеться

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

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


дякую @gbn. Чи можливо оновити свої зовнішні ключі або мені просто доведеться скинути їх і відтворити їх за допомогою пункту ON CASCADE UPDATE?
mounaim

Відкиньте і
відтворіть
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.