Atualização da chave primária em cascata para todas as chaves estrangeiras de referência

11

É possível atualizar um valor da coluna de chave primária com a atualização em cascata entre todas as chaves estrangeiras que o referenciam?

# EDIT 1: Quando executo a consulta followinq

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

, Vejo que update_referential_action está definido como 0. Portanto, nenhuma ação é tomada depois de atualizar minhas colunas de chaves primárias. Como posso atualizar as chaves estrangeiras para torná-las ON CASCADE UPDATE ?

# EDIT 2:
Para criar um script para criação ou remoção de todas as chaves estrangeiras em seu esquema, execute o seguinte script (extraído daqui )

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;  

Para gerar o script de chaves estrangeiras DROP, modifique o valor de @action para ser igual a 'DROP' na cláusula de declaração:

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

Respostas:

9

Se você definiu as restrições de Chave estrangeira como ON UPDATE CASCADEo valor da Chave Primária que foi alterado deve atingir em cascata todas as Chaves Estrangeiras com essa restrição.

Se você não tiver a ON UPDATE CASCADErestrição, precisará criar scripts para concluir a atualização.

EDIT: Como você não tem a ON UPDATE CASCADErestrição, mas deseja configurá-la, é um pouco de trabalho. O SQL Server não oferece suporte à alteração das restrições para uma nova configuração.

É necessário percorrer cada tabela que possui uma restrição FK para a tabela PK. Para cada tabela com o FK:

  1. ALTER TABLE para eliminar a restrição FK existente.
  2. ALTER TABLE novamente para criar a restrição ON UPDATE CASCADE para o FK em questão.

Isso exige um pouco de esforço, mas resultaria em uma restrição adequada para o seu caso.

EDIT 2: As informações necessárias são encontradas em sys.foreign_keys. Você pode selecionar nessa tabela para obter todas as informações necessárias.

Um post de John Paul Cook pode ser encontrado aqui:

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

Esse código eliminará e criará TODAS as restrições de FK em um banco de dados. Você deve poder trabalhar com isso para fazer apenas as alterações desejadas no seu banco de dados.

RLF
fonte
veja minha edição para mais informações
mounaim
Você sabe como script todas as chaves estrangeiras @RLF?
Mounaim
@mounaim - Atualizado com uma nota sobre a criação do script.
RLF
Eu estava trabalhando na mesma coisa e mesmo link ver a minha edição @RLF
mounaim
1
é melhor para incluir blocos de código aqui na DBA SE porque links para outros sites podem quebrar mais tarde :)
mounaim
4

Você com certeza pode. ON UPDATE CASCADEé o que você está procurando.

Aqui está um pequeno tutorial: http://sqlandme.com/2011/08/08/sql-server-how-to-cascade-updates-and-deletes-to-related-tables/

Basicamente, quando você modifica o PK, a cascata sai e atualiza todos os FKs que fazem referência a ele. Isso pode ser feito em sua CREATEdeclaração, como se você estivesse fazendo umaCASCADE DELETE

Fique de olho nas coisas ao fazer isso porque, como eu o entendo, o CASCADE realmente é executado no nível de isolamento SERIALIZABLE(normalmente, o SQL é executado READ COMMITTEDpor padrão) nos bastidores, portanto, observe os problemas de bloqueio.

Informações adicionais sobre os níveis de isolamento podem ser encontradas neste artigo: http://msdn.microsoft.com/en-us/library/ms173763.aspx

Kris Gruttemeyer
fonte
3

Defina todas as chaves estrangeiras como CASCADE UPDATE

Se você não tiver feito isso, terá que

  1. Crie uma nova linha com a nova chave primária
  2. Atualizar todas as tabelas filho
  3. Remover linha antiga

.. em uma transação, é claro, e observando outras restrições que podem falhar

gbn
fonte
obrigado @gbn. É possível atualizar minhas chaves estrangeiras ou só preciso removê-las e recriá-las com a cláusula ON CASCADE UPDATE?
mounaim
Descartar e recriar ...
gbn