Aggiornamento in cascata della chiave primaria a tutte le chiavi esterne di riferimento


11

È possibile aggiornare un valore di colonna chiave primaria con l'aggiornamento a cascata tra tutte le chiavi esterne che lo fanno riferimento?

# EDIT 1: quando eseguo la query followinq

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

, Vedo che update_referential_action è impostato su 0. Pertanto NESSUNA AZIONE viene presa dopo l'aggiornamento delle colonne delle mie chiavi primarie. Come posso aggiornare le chiavi esterne per renderle AGGIORNATE SU CASCADE ?

# EDIT 2:
per eseguire lo script della creazione o l'eliminazione di tutte le chiavi esterne nello schema, eseguire il seguente script (preso da qui )

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;  

Per generare lo script di chiavi esterne DROP, modificare il valore @action in modo che sia uguale a 'DROP' nella clausola di dichiarazione:

DECLARE @action char(6) = 'DROP';

Risposte:


9

Se sono stati definiti i vincoli della chiave esterna come ON UPDATE CASCADEallora il valore della chiave primaria che è stato modificato dovrebbe essere ridotto a tutte le chiavi esterne con quel vincolo.

Se non si dispone del ON UPDATE CASCADEvincolo, sarà necessario creare script per completare l'aggiornamento.

EDIT: Dal momento che non hai il ON UPDATE CASCADEvincolo, ma vuoi averlo impostato, è un po 'di lavoro. SQL Server non supporta l'alterazione dei vincoli a una nuova impostazione.

È necessario scorrere ogni tabella che ha un vincolo FK sulla tabella PK. Per ogni tabella con l'FK:

  1. ALTER TABLE per eliminare il vincolo FK esistente.
  2. ALTER TABLE di nuovo per creare il vincolo ON UPDATE CASCADE per l'FK in questione.

Questo richiede un po 'di sforzo, ma porterebbe a impostare correttamente il vincolo per il tuo caso.

MODIFICA 2: le informazioni necessarie sono disponibili in sys.foreign_keys. È possibile selezionare da quella tabella per ottenere tutte le informazioni necessarie.

Un post di John Paul Cook può essere trovato qui:

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

Questo codice eliminerà e creerà TUTTI i vincoli FK in un database. Dovresti essere in grado di lavorare da quello per apportare solo le modifiche desiderate nel tuo database.


vedere la mia modifica per ulteriori informazioni
mounaim

Sai come eseguire lo script di tutte le chiavi esterne @RLF?
Mounaim,

@mounaim: aggiornato con una nota sulla creazione dello script.
RLF,

Stavo lavorando sulla stessa cosa e sullo stesso link vedi la mia modifica @RLF
mounaim

1
è meglio includere blocchi di codice qui su DBA SE perché i collegamenti ad altri siti Web potrebbero interrompersi più tardi :)
mounaim

4

Sicuro che puoi. ON UPDATE CASCADEè quello che stai cercando.

Ecco un piccolo tutorial: http://sqlandme.com/2011/08/08/sql-server-how-to-cascade-updates-and-deletes-to-related-tables/

Fondamentalmente, quando si modifica il PK, la cascata si spegne e aggiorna tutti gli FK che lo fanno riferimento. Questo può essere fatto nella tua CREATEdichiarazione, come se stessi facendo unCASCADE DELETE

Tieni d'occhio le cose quando lo fai perché, a quanto ho capito, CASCADE in realtà funziona a livello di isolamento SERIALIZABLE(normalmente, SQL funziona READ COMMITTEDdi default) dietro le quinte, quindi osserva eventuali problemi di blocco.

Ulteriori informazioni sui livelli di isolamento sono disponibili in questo articolo: http://msdn.microsoft.com/en-us/library/ms173763.aspx


3

Definire tutte le chiavi esterne come AGGIORNAMENTO CASCADE

Se non l'hai ancora fatto, allora dovrai farlo

  1. Crea una nuova riga con la nuova chiave primaria
  2. Aggiorna tutte le tabelle figlio
  3. Rimuovi la vecchia riga

.. in una transazione ovviamente e attenzione ad altri vincoli che potrebbero fallire


grazie @gbn. È possibile aggiornare le mie chiavi esterne o devo semplicemente rilasciarle e ricrearle con la clausola ON CASCADE UPDATE?
Mounaim,

Rilascia e ricrea ...
gbn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.