Mise à jour en cascade de la clé primaire vers toutes les clés étrangères de référence


11

Est-il possible de mettre à jour une valeur de colonne de clé primaire en cascadant la mise à jour parmi toutes les clés étrangères qui la référencent?

# EDIT 1: lorsque j'exécute une requête de suivi

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

, Je vois que update_referential_action est mis à 0. Ainsi, AUCUNE ACTION n'est prise après la mise à jour de mes colonnes de clés primaires. Comment puis-je mettre à jour les clés étrangères pour les rendre À LA MISE À JOUR EN CASCADE ?

# EDIT 2:
Afin de créer un script pour la création ou la suppression de toutes les clés étrangères dans votre schéma, exécutez le script suivant (extrait d' ici )

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;  

Pour générer le script de clés étrangères DROP, modifiez la valeur @action pour qu'elle soit égale à 'DROP' dans la clause de déclaration:

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

Réponses:


9

Si vous avez défini les contraintes clés étrangères comme ON UPDATE CASCADEla valeur de clé primaire qui a été modifiée devrait cascade vers le bas pour toutes les clés étrangères avec cette contrainte.

Si vous ne disposez pas de la ON UPDATE CASCADEcontrainte, vous devrez créer des scripts pour terminer la mise à jour.

EDIT: Puisque vous ne disposez pas de la ON UPDATE CASCADEcontrainte, mais vous voulez avoir cette mise en place, il est un peu de travail. SQL Server ne prend pas en charge la modification des contraintes d'un nouveau paramètre.

Il est nécessaire de parcourir chaque table qui a une contrainte FK à la table PK. Pour chaque table avec le FK:

  1. ALTER TABLE pour supprimer la contrainte FK existante.
  2. ALTER TABLE à nouveau pour créer la contrainte ON UPDATE CASCADE pour le FK en question.

Cela prend un peu d'effort, mais entraînerait que votre contrainte soit correctement définie pour votre cas.

EDIT 2: Les informations dont vous avez besoin se trouvent dans sys.foreign_keys. Vous pouvez sélectionner dans ce tableau pour obtenir toutes les informations dont vous avez besoin.

Un article de John Paul Cook peut être trouvé ici:

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

Ce code supprimera et créera TOUTES les contraintes FK dans une base de données. Vous devriez pouvoir travailler à partir de cela pour effectuer uniquement les modifications souhaitées dans votre base de données.


voir mon montage pour plus d'informations
mounaim

Savez-vous comment écrire toutes les clés étrangères @RLF?
mounaim

@mounaim - Mis à jour avec une note sur la création du script.
RLF

Je travaillais sur la même chose et le même lien voir ma modification @RLF
mounaim

1
il est préférable d'inclure des blocs de code ici sur DBA SE car les liens vers d'autres sites Web peuvent se briser plus tard :)
mounaim

4

Vous pouvez certainement. ON UPDATE CASCADEest ce que vous recherchez.

Voici un petit guide pratique: http://sqlandme.com/2011/08/08/sql-server-how-to-cascade-updates-and-deletes-to-related-tables/

Fondamentalement, lorsque vous modifiez le PK, la cascade s'éteint et met à jour tous les FK qui le référencent. Cela peut être fait dans votre CREATEdéclaration, comme si vous faisiez unCASCADE DELETE

Gardez un œil sur les choses lorsque vous faites cela car, si je comprends bien, CASCADE s'exécute en fait au niveau d'isolement SERIALIZABLE(normalement, SQL s'exécute READ COMMITTEDpar défaut) en arrière-plan, alors surveillez les problèmes de blocage.

Des informations supplémentaires sur les niveaux d'isolement sont disponibles dans cet article: http://msdn.microsoft.com/en-us/library/ms173763.aspx


3

Définissez toutes les clés étrangères comme CASCADE UPDATE

Si vous ne l'avez pas fait, alors vous devrez

  1. Créer une nouvelle ligne avec une nouvelle clé primaire
  2. Mettre à jour toutes les tables enfants
  3. Supprimer l'ancienne ligne

.. dans une transaction bien sûr et attention aux autres contraintes qui pourraient échouer


merci @gbn. Est-il possible de mettre à jour mes clés étrangères ou je dois simplement les supprimer et les recréer avec la clause ON CASCADE UPDATE?
mounaim

Déposer et recréer ...
gbn
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.