web-dev-qa-db-ja.com

参照するすべての外部キーへの主キー更新のカスケード

それを参照するすべての外部キー間で更新をカスケードして主キー列の値を更新することは可能ですか?

#EDIT 1:followinq queryを実行すると

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

、update_referential_actionが0に設定されていることがわかります。したがって、主キー列を更新した後は何も行われません。外部キーを更新してON CASCADE UPDATEにするにはどうすればよいですか?

#編集2:
スキーマ内のすべての外部キーの作成または削除をスクリプト化するには、次のスクリプトを実行します( here から取得)

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';
11
mounaim

外部キー制約をON UPDATE CASCADEとして定義した場合、変更された主キー値は、その制約を持つすべての外部キーにカスケードダウンする必要があります。

ON UPDATE CASCADE制約がない場合は、更新を完了するためのスクリプトを作成する必要があります。

編集:ON UPDATE CASCADE制約はありませんが、それを設定したいので、これは少しの作業です。 SQL Serverは、制約を新しい設定に変更することをサポートしていません。

PKテーブルに対するFK制約を持つ各テーブルを反復処理する必要があります。 FKを含む各テーブルについて:

  1. 既存のFK制約を削除するALTER TABLE。
  2. 問題のFKのON UPDATE CASCADE制約を作成するには、ALTER TABLEを再度実行します。

これには少し手間がかかりますが、ケースに応じて制約が適切に設定されます。

編集2:必要な情報はsys.foreign_keysにあります。そのテーブルから選択して、必要なすべての情報を取得できます。

ジョンポールクックの投稿はこちらです。

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

このコードは、データベース内のすべてのFK制約を削除して作成します。データベースから必要な変更のみを加えることができるはずです。

9
RLF

できます。 ON UPDATE CASCADEはあなたが探しているものです。

ここに小さなハウツーがあります: http://sqlandme.com/2011/08/08/sql-server-how-to-cascade-updates-and-deletes-to-related-tables/

基本的に、PKを変更すると、カスケードが実行され、それを参照するすべてのFKが更新されます。これは、CASCADE DELETEを実行する場合と同じように、CREATEステートメントで実行できます。

CASCADEは実際にはバックグラウンドで分離レベルSERIALIZABLE(通常、SQLはデフォルトでREAD COMMITTEDで実行されます)で実行されるため、これを行うときは注意してください。ブロッキングの問題。

分離レベルに関する追加情報はこの記事にあります: http://msdn.Microsoft.com/en-us/library/ms173763.aspx

4

すべての外部キーをCASCADE UPDATEとして定義します

これを行っていない場合は、

  1. 新しい主キーで新しい行を作成する
  2. すべての子テーブルを更新する
  3. 古い行を削除

..もちろんトランザクションで、失敗する可能性のある他の制約に注意する

3
gbn