Kaskadowa aktualizacja klucza podstawowego do wszystkich odwołujących się kluczy obcych

11

Czy można zaktualizować wartość kolumny klucza podstawowego poprzez kaskadowanie aktualizacji wśród wszystkich kluczy obcych, do których się ona odnosi?

# EDYCJA 1: Po uruchomieniu zapytania followinq

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

, Widzę, że aktualizacja_referencyjna aktywność jest ustawiona na 0. W związku z tym po zaktualizowaniu kolumn moich kluczy podstawowych nie są podejmowane żadne działania. Jak mogę zaktualizować klucze obce, aby były W AKTUALIZACJI KASKADY ?

# EDYCJA 2:
Aby wykonać skrypt tworzenia lub upuszczania wszystkich kluczy obcych w schemacie, uruchom następujący skrypt (pobrany stąd )

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;  

Aby wygenerować skrypt kluczy obcych DROP, zmodyfikuj wartość @action, aby była równa „DROP” w klauzuli deklaracji:

DECLARE @action char(6) = 'DROP';
Mounaim
źródło

Odpowiedzi:

9

Jeśli zdefiniowano ograniczenia klucza obcego jako ON UPDATE CASCADEzmienione, wartość klucza podstawowego powinna zostać kaskadowo zmniejszona do wszystkich kluczy obcych z tym ograniczeniem.

Jeśli nie masz ON UPDATE CASCADEograniczenia, musisz utworzyć skrypty, aby dokończyć aktualizację.

EDYCJA: Ponieważ nie masz ON UPDATE CASCADEograniczeń, ale chcesz je skonfigurować, jest to trochę pracy. SQL Server nie obsługuje zmiany ograniczeń do nowego ustawienia.

Konieczne jest iterowanie każdej tabeli, która ma ograniczenie FK do tabeli PK. Dla każdego stołu z FK:

  1. ZMIEŃ TABELĘ, aby usunąć istniejące ograniczenie FK.
  2. ALTER TABLE ponownie, aby utworzyć ograniczenie ON UPDATE CASCADE dla danego FK.

To wymaga trochę wysiłku, ale spowodowałoby prawidłowe ustawienie ograniczenia dla twojego przypadku.

EDYCJA 2: Potrzebne informacje można znaleźć w sys.foreign_keys. Możesz wybrać z tej tabeli, aby uzyskać wszystkie potrzebne informacje.

Post od Johna Paula Cooka można znaleźć tutaj:

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

Ten kod zostanie usunięty i utworzy WSZYSTKIE ograniczenia FK w bazie danych. Powinieneś być w stanie pracować z tym, aby dokonywać tylko tych zmian, które chcesz w bazie danych.

RLF
źródło
zobacz moją edycję, aby uzyskać więcej informacji
mounaim,
Czy wiesz, jak wykonać skrypt dla wszystkich kluczy obcych @RLF?
mounaim
@mounaim - Zaktualizowano z uwagą na temat tworzenia skryptu.
RLF,
Pracowałem nad tym samym i ten sam link, patrz moja edycja @RLF
mounaim
1
lepiej to bloki kodu tutaj na DBA SE ponieważ linki do innych stron internetowych może złamać później :)
mounaim
4

Na pewno możesz. ON UPDATE CASCADEjest tym, czego szukasz.

Oto małe instrukcje: http://sqlandme.com/2011/08/08/sql-server-how-to-cascade-updates-and-deletes-to-related-tables/

Zasadniczo po zmodyfikowaniu PK kaskada zniknie i zaktualizuje wszystkie FK, które się do niej odwołują. Można to zrobić w swoim CREATEoświadczeniu, tak jakbyś robiłCASCADE DELETE

Wykonuj te czynności, ponieważ, jak rozumiem, CASCADE faktycznie działa na poziomie izolacji SERIALIZABLE(normalnie SQL działa READ COMMITTEDdomyślnie) za scenami, więc uważaj na wszelkie problemy z blokowaniem.

Dodatkowe informacje na temat poziomów izolacji można znaleźć w tym artykule: http://msdn.microsoft.com/en-us/library/ms173763.aspx

Kris Gruttemeyer
źródło
3

Zdefiniuj wszystkie klucze obce jako CASCADE UPDATE

Jeśli tego nie zrobiłeś, musisz to zrobić

  1. Utwórz nowy wiersz z nowym kluczem podstawowym
  2. Zaktualizuj wszystkie tabele podrzędne
  3. Usuń stary wiersz

.. oczywiście w transakcji i uważając na inne ograniczenia, które mogą zawieść

gbn
źródło
dzięki @gbn. Czy można zaktualizować moje klucze obce, czy po prostu muszę je upuścić i odtworzyć za pomocą klauzuli ON CASCADE UPDATE?
mounaim
Upuść i odtwórz ...
gbn