web-dev-qa-db-fra.com

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

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 y font référence?

# EDIT 1: Lorsque j'exécute la requête followinq

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 (tiré de 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';
11
mounaim

Si vous avez défini les contraintes de clé étrangère comme ON UPDATE CASCADE, la valeur de la clé primaire qui a été modifiée doit se répercuter sur toutes les clés étrangères avec cette contrainte.

Si vous n'avez pas le ON UPDATE CASCADE contrainte, vous devrez alors créer des scripts pour terminer la mise à jour.

EDIT: Puisque vous n'avez pas le ON UPDATE CASCADE contrainte, mais vous voulez que cela soit configuré, c'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.

9
RLF

Vous pouvez certainement. ON UPDATE CASCADE est 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 instruction CREATE, comme si vous faisiez un CASCADE 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 COMMITTED par défaut) dans les coulisses, alors surveillez les problèmes de blocage.

Des informations supplémentaires sur les niveaux d'isolement peuvent être trouvées dans cet article: http://msdn.Microsoft.com/en-us/library/ms173763.aspx

4
Kris Gruttemeyer

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

3
gbn