Como as restrições de chave estrangeira podem ser desabilitadas temporariamente usando o T-SQL?

824

Desabilitar e habilitar restrições de chave estrangeira são suportadas no SQL Server? Ou é a minha única opção para drope depois refazercreate as restrições?

Raio
fonte
128
Para as pessoas que perguntam "por que" eu gostaria de fazer o seguinte: É para um ambiente de teste em que desejo remover e carregar dados de teste de várias tabelas sem precisar manter e especificar a ordem de carregamento dos dados. A integridade dos dados não é tão importante para esse cenário.
Ray
8
Nota - se você quiser TRUNCAR a tabela, precisará eliminar realmente as restrições.
OutstandingBill
@OutstandingBill Aparentemente, isso funciona para TRUNCATE .
jpaugh
1
Parece estranho que alguém questione isso em um ambiente de produção . Um caso de uso muito comum é para inserções em massa. Se você possui uma tabela autorreferencial, às vezes é extremamente difícil classificar uma inserção em massa para que a linha pai sempre seja inserida antes do filho, para desativar a restrição, a inserção em massa e ativar a restrição.
Auspex

Respostas:

1126

Se você deseja desativar todas as restrições no banco de dados, basta executar este código:

-- disable all constraints
EXEC sp_MSforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"

Para ativá-los novamente, execute: (a impressão é opcional, é claro, e está apenas listando as tabelas)

-- enable all constraints
exec sp_MSforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

Acho útil ao preencher dados de um banco de dados para outro. É uma abordagem muito melhor do que eliminar restrições. Como você mencionou, é útil ao descartar todos os dados no banco de dados e preenchê-los novamente (digamos, no ambiente de teste).

Se você estiver excluindo todos os dados, poderá achar útil esta solução .

Às vezes, também é útil desativar todos os gatilhos. Você pode ver a solução completa aqui .

Kristof
fonte
9
"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"Deveria haver apenas um "CHECK" lá?
CrazyPyro
27
@CrazyPyro - não você precisa de ambos
kristof
32
@ CrazyPyro: ambos são realmente necessários, razão para isso porque o primeiro CHECK pertence ao WITH e o segundo CHECK ao CONSTRAINT (é o tipo de restrição). A primeira verificação garante que seus dados sejam verificados quanto à consistência ao ativar a restrição. Se você não quiser, pode escrever WITH NOCHECK. Pode ser útil em determinadas situações de teste quando você não se importa com os dados reais, desde que haja alguns para que suas consultas tenham algo com que brincar.
Valentino Vranken
8
É ruim que eu consiga esse resultado no segundo comando? "A declaração ALTER TABLE entrou em conflito com a restrição FOREIGN KEY ..."
Advogado do Diabo
34
Vale a pena notar que, mesmo com as restrições desativadas, o TRUNCATE TABLE não funcionará. Para isso, você precisará eliminar as restrições. Caso contrário, use DELETE FROM, mas leve em consideração a diferença: mssqltips.com/sqlservertip/1080/…
James McCormack
404

http://www.sqljunkies.com/WebLog/roman/archive/2005/01/30/7037.aspx

-- Disable all table constraints

ALTER TABLE MyTable NOCHECK CONSTRAINT ALL

-- Enable all table constraints

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT ALL

-- Disable single constraint

ALTER TABLE MyTable NOCHECK CONSTRAINT MyConstraint

-- Enable single constraint

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint
ScottStonehouse
fonte
29
um bom encontrar, mas nota que você ainda não pode truncar a tabela sem remover as restrições de chave estrangeira
Steven A. Lowe
1
e você também precisará estar ciente de que, ao reativar as restrições e fazer uma verificação de integridade dos dados, seus dados podem falhar e corrigir um problema como esse pode ser um pesadelo se os dados com falha estiverem no final de uma longa sequência de restrições vinculadas.
217 Jimoc
1
Você também precisa de uma segunda verificação ao ativar as restrições novamente. Caso contrário, como está, seu código verificará a restrição apenas uma vez, e não a ativará.
Ps2goat
3
sim "necessário com verificação de cheque" para 2012. A edição foi rejeitada? MS Fazer a ligação
crokusek
1
As instruções usadas aqui para reativar as restrições estão ausentes nas cláusulas WITH CHECK. Esta é uma falha bastante importante. Veja minha resposta para obter detalhes - stackoverflow.com/a/35427150/81595 .
Scott Munro
37

Para desabilitar a restrição, você tem ALTERa tabela usando NOCHECK

ALTER TABLE [TABLE_NAME] NOCHECK CONSTRAINT [ALL|CONSTRAINT_NAME]

Para permitir que você precise usar o CHECK duplo :

ALTER TABLE [TABLE_NAME] WITH CHECK CHECK CONSTRAINT [ALL|CONSTRAINT_NAME]
  • Preste atenção ao duplo CHECK CHECK ao ativar.
  • ALL significa para todas as restrições na tabela.

Depois de concluído, se você precisar verificar o status, use este script para listar o status da restrição. Será muito útil:

    SELECT (CASE 
        WHEN OBJECTPROPERTY(CONSTID, 'CNSTISDISABLED') = 0 THEN 'ENABLED'
        ELSE 'DISABLED'
        END) AS STATUS,
        OBJECT_NAME(CONSTID) AS CONSTRAINT_NAME,
        OBJECT_NAME(FKEYID) AS TABLE_NAME,
        COL_NAME(FKEYID, FKEY) AS COLUMN_NAME,
        OBJECT_NAME(RKEYID) AS REFERENCED_TABLE_NAME,
        COL_NAME(RKEYID, RKEY) AS REFERENCED_COLUMN_NAME
   FROM SYSFOREIGNKEYS
ORDER BY TABLE_NAME, CONSTRAINT_NAME,REFERENCED_TABLE_NAME, KEYNO 
Diego Mendes
fonte
Não ver para chaves primárias ? Para chaves estrangeiras SYSFOREIGNKEYS System View sys.sysforeignkeys msdn.microsoft.com/en-us/library/ms177604.aspx
Kiquenet
se você está tentando desativar chave primária para inserir, eu recomendaria usando (SET IDENTITY_INSERT) se você quiser apenas verificar se há chave primária, você pode tentar sys.key_constraints com sys.indexes.is_primary_key
Diego Mendes
29

Sua melhor opção é DROP e CRIAR restrições de chave estrangeira.

Não encontrei exemplos neste post que funcionariam para mim "como estão", um não funcionaria se chaves estrangeiras fizessem referência a esquemas diferentes, o outro não funcionaria se chaves estrangeiras fizessem referência a várias colunas. Este script considera os dois esquemas múltiplos e as colunas múltiplas por chave estrangeira.

Aqui está o script que gera instruções "ADD CONSTRAINT", para várias colunas as separará por vírgula ( salve essa saída antes de executar as instruções DROP ):

PRINT N'-- CREATE FOREIGN KEY CONSTRAINTS --';

SET NOCOUNT ON;
SELECT '
PRINT N''Creating '+ const.const_name +'...''
GO
ALTER TABLE ' + const.parent_obj + '
    ADD CONSTRAINT ' + const.const_name + ' FOREIGN KEY (
            ' + const.parent_col_csv + '
            ) REFERENCES ' + const.ref_obj + '(' + const.ref_col_csv + ')
GO'
FROM (
    SELECT QUOTENAME(fk.NAME) AS [const_name]
        ,QUOTENAME(schParent.NAME) + '.' + QUOTENAME(OBJECT_name(fkc.parent_object_id)) AS [parent_obj]
        ,STUFF((
                SELECT ',' + QUOTENAME(COL_NAME(fcP.parent_object_id, fcp.parent_column_id))
                FROM sys.foreign_key_columns AS fcP
                WHERE fcp.constraint_object_id = fk.object_id
                FOR XML path('')
                ), 1, 1, '') AS [parent_col_csv]
        ,QUOTENAME(schRef.NAME) + '.' + QUOTENAME(OBJECT_NAME(fkc.referenced_object_id)) AS [ref_obj]
        ,STUFF((
                SELECT ',' + QUOTENAME(COL_NAME(fcR.referenced_object_id, fcR.referenced_column_id))
                FROM sys.foreign_key_columns AS fcR
                WHERE fcR.constraint_object_id = fk.object_id
                FOR XML path('')
                ), 1, 1, '') AS [ref_col_csv]
    FROM sys.foreign_key_columns AS fkc
    INNER JOIN sys.foreign_keys AS fk ON fk.object_id = fkc.constraint_object_id
    INNER JOIN sys.objects AS oParent ON oParent.object_id = fkc.parent_object_id
    INNER JOIN sys.schemas AS schParent ON schParent.schema_id = oParent.schema_id
    INNER JOIN sys.objects AS oRef ON oRef.object_id = fkc.referenced_object_id
    INNER JOIN sys.schemas AS schRef ON schRef.schema_id = oRef.schema_id
    GROUP BY fkc.parent_object_id
        ,fkc.referenced_object_id
        ,fk.NAME
        ,fk.object_id
        ,schParent.NAME
        ,schRef.NAME
    ) AS const
ORDER BY const.const_name

Aqui está o script que gera instruções "DROP CONSTRAINT":

PRINT N'-- DROP FOREIGN KEY CONSTRAINTS --';

SET NOCOUNT ON;

SELECT '
PRINT N''Dropping ' + fk.NAME + '...''
GO
ALTER TABLE [' + sch.NAME + '].[' + OBJECT_NAME(fk.parent_object_id) + ']' + ' DROP  CONSTRAINT ' + '[' + fk.NAME + ']
GO'
FROM sys.foreign_keys AS fk
INNER JOIN sys.schemas AS sch ON sch.schema_id = fk.schema_id
ORDER BY fk.NAME
vic
fonte
5
Você pode explicar por que é melhor do que desabilitar e reativar restrições?
Mahmoodvcs
Bom roteiro. Para uma abordagem semelhante, mas alternativo, veja: mssqltips.com/sqlservertip/3347/...
Matt Browne
11

O padrão SQL-92 permite que uma restrição seja declarada como DEFERRABLE, para que possa ser adiada (implícita ou explicitamente) dentro do escopo de uma transação. Infelizmente, o SQL Server ainda está faltando essa funcionalidade do SQL-92.

Para mim, alterar uma restrição para NOCHECK é semelhante à alteração imediata da estrutura do banco de dados - certamente é necessário eliminar restrições - e algo a ser evitado (por exemplo, os usuários exigem privilégios aumentados).

um dia quando
fonte
11
   --Drop and Recreate Foreign Key Constraints

SET NOCOUNT ON

DECLARE @table TABLE(
   RowId INT PRIMARY KEY IDENTITY(1, 1),
   ForeignKeyConstraintName NVARCHAR(200),
   ForeignKeyConstraintTableSchema NVARCHAR(200),
   ForeignKeyConstraintTableName NVARCHAR(200),
   ForeignKeyConstraintColumnName NVARCHAR(200),
   PrimaryKeyConstraintName NVARCHAR(200),
   PrimaryKeyConstraintTableSchema NVARCHAR(200),
   PrimaryKeyConstraintTableName NVARCHAR(200),
   PrimaryKeyConstraintColumnName NVARCHAR(200)    
)

INSERT INTO @table(ForeignKeyConstraintName, ForeignKeyConstraintTableSchema, ForeignKeyConstraintTableName, ForeignKeyConstraintColumnName)
SELECT 
   U.CONSTRAINT_NAME, 
   U.TABLE_SCHEMA, 
   U.TABLE_NAME, 
   U.COLUMN_NAME 
FROM 
   INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
         ON U.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
   C.CONSTRAINT_TYPE = 'FOREIGN KEY'

UPDATE @table SET
   PrimaryKeyConstraintName = UNIQUE_CONSTRAINT_NAME
FROM 
   @table T
      INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R
         ON T.ForeignKeyConstraintName = R.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintTableSchema  = TABLE_SCHEMA,
   PrimaryKeyConstraintTableName  = TABLE_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
      ON T.PrimaryKeyConstraintName = C.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintColumnName = COLUMN_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      ON T.PrimaryKeyConstraintName = U.CONSTRAINT_NAME

--SELECT * FROM @table

--DROP CONSTRAINT:
SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   DROP CONSTRAINT ' + ForeignKeyConstraintName + '

   GO'
FROM
   @table

--ADD CONSTRAINT:
SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   ADD CONSTRAINT ' + ForeignKeyConstraintName + ' FOREIGN KEY(' + ForeignKeyConstraintColumnName + ') REFERENCES [' + PrimaryKeyConstraintTableSchema + '].[' + PrimaryKeyConstraintTableName + '](' + PrimaryKeyConstraintColumnName + ')

   GO'
FROM
   @table

GO

Eu concordo com você, Hamlin. Quando você transfere dados usando o SSIS ou quando deseja replicar dados, parece bastante necessário desativar temporariamente ou eliminar restrições de chave estrangeira e depois reativá-las ou recriá-las. Nesses casos, a integridade referencial não é um problema, porque já é mantida no banco de dados de origem. Portanto, você pode ter certeza sobre esse assunto.

Amir Hussein Samiani
fonte
Esse script é ótimo para gerar meus comandos "ALTER", mas como posso executá-los / executados em um SP?
BlueChippy
1
Eu acho que isso não vai funcionar se qualquer uma das chaves estrangeiras é multi-coluna
Zar Shardan
Isso também não produziu todos os caracteres para nomes de tabela / chave excessivamente longos.
21413 Joshua Drake
11
SET NOCOUNT ON

DECLARE @table TABLE(
   RowId INT PRIMARY KEY IDENTITY(1, 1),
   ForeignKeyConstraintName NVARCHAR(200),
   ForeignKeyConstraintTableSchema NVARCHAR(200),
   ForeignKeyConstraintTableName NVARCHAR(200),
   ForeignKeyConstraintColumnName NVARCHAR(200),
   PrimaryKeyConstraintName NVARCHAR(200),
   PrimaryKeyConstraintTableSchema NVARCHAR(200),
   PrimaryKeyConstraintTableName NVARCHAR(200),
   PrimaryKeyConstraintColumnName NVARCHAR(200),
   UpdateRule NVARCHAR(100),
   DeleteRule NVARCHAR(100)   
)

INSERT INTO @table(ForeignKeyConstraintName, ForeignKeyConstraintTableSchema, ForeignKeyConstraintTableName, ForeignKeyConstraintColumnName)
SELECT 
   U.CONSTRAINT_NAME, 
   U.TABLE_SCHEMA, 
   U.TABLE_NAME, 
   U.COLUMN_NAME
FROM 
   INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
         ON U.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
   C.CONSTRAINT_TYPE = 'FOREIGN KEY'

UPDATE @table SET
   T.PrimaryKeyConstraintName = R.UNIQUE_CONSTRAINT_NAME,
   T.UpdateRule = R.UPDATE_RULE,
   T.DeleteRule = R.DELETE_RULE
FROM 
   @table T
      INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R
         ON T.ForeignKeyConstraintName = R.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintTableSchema  = TABLE_SCHEMA,
   PrimaryKeyConstraintTableName  = TABLE_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
      ON T.PrimaryKeyConstraintName = C.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintColumnName = COLUMN_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      ON T.PrimaryKeyConstraintName = U.CONSTRAINT_NAME

--SELECT * FROM @table

SELECT '
BEGIN TRANSACTION
BEGIN TRY'

--DROP CONSTRAINT:
SELECT
   '
 ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
 DROP CONSTRAINT ' + ForeignKeyConstraintName + '
   '
FROM
   @table

SELECT '
END TRY

BEGIN CATCH
   ROLLBACK TRANSACTION
   RAISERROR(''Operation failed.'', 16, 1)
END CATCH

IF(@@TRANCOUNT != 0)
BEGIN
   COMMIT TRANSACTION
   RAISERROR(''Operation completed successfully.'', 10, 1)
END
'

--ADD CONSTRAINT:
SELECT '
BEGIN TRANSACTION
BEGIN TRY'

SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   ADD CONSTRAINT ' + ForeignKeyConstraintName + ' FOREIGN KEY(' + ForeignKeyConstraintColumnName + ') REFERENCES [' + PrimaryKeyConstraintTableSchema + '].[' + PrimaryKeyConstraintTableName + '](' + PrimaryKeyConstraintColumnName + ') ON UPDATE ' + UpdateRule + ' ON DELETE ' + DeleteRule + '
   '
FROM
   @table

SELECT '
END TRY

BEGIN CATCH
   ROLLBACK TRANSACTION
   RAISERROR(''Operation failed.'', 16, 1)
END CATCH

IF(@@TRANCOUNT != 0)
BEGIN
   COMMIT TRANSACTION
   RAISERROR(''Operation completed successfully.'', 10, 1)
END'

GO
Amir Hussein Samiani
fonte
10

WITH CHECK CHECK é quase certamente necessário!

Este ponto foi levantado em algumas das respostas e comentários, mas acho que é importante o suficiente para chamá-lo novamente.

A reativação de uma restrição usando o seguinte comando (no WITH CHECK) terá algumas desvantagens sérias .

ALTER TABLE MyTable CHECK CONSTRAINT MyConstraint;

COM CHEQUE | COM NOCHECK

Especifica se os dados na tabela são ou não validados com relação a uma restrição FOREIGN KEY ou CHECK recém-adicionada ou reativada. Se não especificado, WITH CHECK será assumido para novas restrições e WITH NOCHECK será assumido para restrições reativadas.

Se você não deseja verificar as novas restrições CHECK ou FOREIGN KEY em relação aos dados existentes, use WITH NOCHECK. Não recomendamos fazer isso, exceto em casos raros. A nova restrição será avaliada em todas as atualizações de dados posteriores. Quaisquer violações de restrição suprimidas por WITH NOCHECK quando a restrição é adicionada podem causar falhas em atualizações futuras se atualizarem linhas com dados que não estão em conformidade com a restrição.

O otimizador de consulta não considera restrições definidas com WITH NOCHECK. Tais restrições são ignoradas até serem reativadas usando a tabela ALTER TABLE WITH CHECK CHECK CONSTRAINT ALL.

Nota: WITH NOCHECK é o padrão para reativar restrições. Eu tenho que me perguntar por que ...

  1. Nenhum dado existente na tabela será avaliado durante a execução deste comando - a conclusão bem-sucedida não garante que os dados na tabela sejam válidos de acordo com a restrição.
  2. Durante a próxima atualização dos registros inválidos, a restrição será avaliada e falhará - resultando em erros que podem não estar relacionados à atualização real feita.
  3. A lógica do aplicativo que depende da restrição para garantir que os dados sejam válidos pode falhar.
  4. O otimizador de consulta não fará uso de nenhuma restrição ativada dessa maneira.

A exibição do sistema sys.foreign_keys fornece alguma visibilidade do problema. Observe que ele possui is_disableduma is_not_trustedcoluna e uma . is_disabledindica se operações futuras de manipulação de dados serão validadas contra a restrição. is_not_trustedindica se todos os dados atualmente na tabela foram validados com relação à restrição.

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint;

Suas restrições são confiáveis? Descobrir...

SELECT * FROM sys.foreign_keys WHERE is_not_trusted = 1;
Scott Munro
fonte
9

Primeiro post :)

Para o OP, a solução da kristof funcionará, a menos que haja problemas com grandes problemas de dados e balão de log de transações com grandes exclusões. Além disso, mesmo com o armazenamento do tlog de sobra, uma vez que as exclusões são gravadas no tlog, a operação pode demorar muito para tabelas com centenas de milhões de linhas.

Uso uma série de cursores para truncar e recarregar cópias grandes de um de nossos grandes bancos de dados de produção com frequência. A solução projetada é responsável por vários esquemas, várias colunas de chave estrangeira e, o melhor de tudo, pode ser implementada para uso no SSIS.

Envolve a criação de três tabelas intermediárias (tabelas reais) para abrigar os scripts DROP, CREATE e CHECK FK, criação e inserção desses scripts nas tabelas e, em seguida, percorrer as tabelas e executá-las. O script anexado é composto de quatro partes: 1.) criação e armazenamento dos scripts nas três tabelas temporárias (reais), 2.) execução dos scripts drop FK por meio de um cursor, um por um, 3.) Usando sp_MSforeachtable para truncar todos os tabelas no banco de dados que não sejam nossas três tabelas intermediárias e 4.) execução do create FK e verifique os scripts FK no final do seu pacote ETL SSIS.

Execute a parte de criação de script em uma tarefa Executar SQL no SSIS. Execute a parte "execute Drop FK Scripts" em uma segunda tarefa Executar SQL. Coloque o script de truncamento em uma terceira tarefa Execute SQL e, em seguida, execute quaisquer outros processos ETL necessários antes de anexar os scripts CREATE e CHECK em uma tarefa final Execute SQL (ou dois, se desejado) no final do fluxo de controle.

O armazenamento dos scripts em tabelas reais provou ser inestimável quando a re-aplicação das chaves estrangeiras falha, pois você pode selecionar * em sync_CreateFK, copiar / colar na janela de consulta, executá-los um de cada vez e corrigir os problemas de dados assim que você encontre aqueles que falharam / ainda não estão sendo reaplicados.

Não execute novamente o script se ele falhar sem ter a certeza de aplicar novamente todas as chaves / verificações estrangeiras antes de fazê-lo, ou você provavelmente perderá alguma criação e verificará o script fk quando nossas tabelas de preparação forem descartadas e recriado antes da criação dos scripts a serem executados.

----------------------------------------------------------------------------
1)
/*
Author:         Denmach
DateCreated:    2014-04-23
Purpose:        Generates SQL statements to DROP, ADD, and CHECK existing constraints for a 
                database.  Stores scripts in tables on target database for execution.  Executes
                those stored scripts via independent cursors. 
DateModified:
ModifiedBy
Comments:       This will eliminate deletes and the T-log ballooning associated with it.
*/

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;



--------------------------------Generate scripts to drop all foreign keys in a database --------------------------------

IF OBJECT_ID('dbo.sync_dropFK') IS NOT NULL
DROP TABLE sync_dropFK

CREATE TABLE sync_dropFK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )

DECLARE FKcursor CURSOR FOR

    SELECT 
        OBJECT_SCHEMA_NAME(parent_object_id)
        , OBJECT_NAME(parent_object_id)
        , name
    FROM 
        sys.foreign_keys WITH (NOLOCK)
    ORDER BY 
        1,2;

OPEN FKcursor;

FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name

WHILE @@FETCH_STATUS = 0

BEGIN
        SET @tsql = 'ALTER TABLE '
                + QUOTENAME(@schema_name) 
                + '.' 
                + QUOTENAME(@table_name)
                + ' DROP CONSTRAINT ' 
                + QUOTENAME(@constraint_name) 
                + ';';
    --PRINT @tsql;
    INSERT sync_dropFK  (
                        Script
                        )
                        VALUES (
                                @tsql
                                )   

    FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name
    ;

END;

CLOSE FKcursor;

DEALLOCATE FKcursor;


---------------Generate scripts to create all existing foreign keys in a database --------------------------------
----------------------------------------------------------------------------------------------------------
IF OBJECT_ID('dbo.sync_createFK') IS NOT NULL
DROP TABLE sync_createFK

CREATE TABLE sync_createFK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )

IF OBJECT_ID('dbo.sync_createCHECK') IS NOT NULL
DROP TABLE sync_createCHECK

CREATE TABLE sync_createCHECK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )   

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 WITH (NOLOCK)

    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

        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 WITH (NOLOCK)
                INNER JOIN sys.foreign_key_columns fkc WITH (NOLOCK) 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);
            --PRINT '@tsql = ' + @tsql 
            --PRINT '@tsql2 = ' + @tsql2
            FETCH NEXT FROM ColumnCursor INTO @fkCol, @pkCol;
            --PRINT 'FK Column ' + @fkCol
            --PRINT 'PK Column ' + @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
        INSERT sync_createFK    
                        (
                        Script
                        )
                        VALUES (
                                @tsql
                                )

-------------------Generate CHECK CONSTRAINT scripts for a database ------------------------------
----------------------------------------------------------------------------------------------------------

        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;
        INSERT sync_createCHECK 
                        (
                        Script
                        )
                        VALUES (
                                @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;

--SELECT * FROM sync_DropFK
--SELECT * FROM sync_CreateFK
--SELECT * FROM sync_CreateCHECK
---------------------------------------------------------------------------
2.)
-----------------------------------------------------------------------------------------------------------------
----------------------------execute Drop FK Scripts --------------------------------------------------

DECLARE @scriptD NVARCHAR(4000)

DECLARE DropFKCursor CURSOR FOR
    SELECT Script 
    FROM sync_dropFK WITH (NOLOCK)

OPEN DropFKCursor

FETCH NEXT FROM DropFKCursor
INTO @scriptD

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptD
EXEC (@scriptD)
FETCH NEXT FROM DropFKCursor
INTO @scriptD
END
CLOSE DropFKCursor
DEALLOCATE DropFKCursor
--------------------------------------------------------------------------------
3.) 

------------------------------------------------------------------------------------------------------------------
----------------------------Truncate all tables in the database other than our staging tables --------------------
------------------------------------------------------------------------------------------------------------------


EXEC sp_MSforeachtable 'IF OBJECT_ID(''?'') NOT IN 
(
ISNULL(OBJECT_ID(''dbo.sync_createCHECK''),0),
ISNULL(OBJECT_ID(''dbo.sync_createFK''),0),
ISNULL(OBJECT_ID(''dbo.sync_dropFK''),0)
)
BEGIN TRY
 TRUNCATE TABLE ?
END TRY
BEGIN CATCH
 PRINT ''Truncation failed on''+ ? +''
END CATCH;' 
GO
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------
----------------------------execute Create FK Scripts and CHECK CONSTRAINT Scripts---------------
----------------------------tack me at the end of the ETL in a SQL task-------------------------
-------------------------------------------------------------------------------------------------
DECLARE @scriptC NVARCHAR(4000)

DECLARE CreateFKCursor CURSOR FOR
    SELECT Script 
    FROM sync_createFK WITH (NOLOCK)

OPEN CreateFKCursor

FETCH NEXT FROM CreateFKCursor
INTO @scriptC

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptC
EXEC (@scriptC)
FETCH NEXT FROM CreateFKCursor
INTO @scriptC
END
CLOSE CreateFKCursor
DEALLOCATE CreateFKCursor
-------------------------------------------------------------------------------------------------
DECLARE @scriptCh NVARCHAR(4000)

DECLARE CreateCHECKCursor CURSOR FOR
    SELECT Script 
    FROM sync_createCHECK WITH (NOLOCK)

OPEN CreateCHECKCursor

FETCH NEXT FROM CreateCHECKCursor
INTO @scriptCh

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptCh
EXEC (@scriptCh)
FETCH NEXT FROM CreateCHECKCursor
INTO @scriptCh
END
CLOSE CreateCHECKCursor
DEALLOCATE CreateCHECKCursor
Denmach
fonte
7

Encontre a restrição

SELECT * 
FROM sys.foreign_keys
WHERE referenced_object_id = object_id('TABLE_NAME')

Execute o SQL gerado por este SQL

SELECT 
    'ALTER TABLE ' +  OBJECT_SCHEMA_NAME(parent_object_id) +
    '.[' + OBJECT_NAME(parent_object_id) + 
    '] DROP CONSTRAINT ' + name
FROM sys.foreign_keys
WHERE referenced_object_id = object_id('TABLE_NAME')

Maneira segura.

Nota: Solução adicionada para eliminar a restrição para que a tabela possa ser eliminada ou modificada sem nenhum erro de restrição.

Aditya
fonte
3

Clique com o botão direito do mouse no design da tabela e vá para Relacionamentos e escolha a chave estrangeira no painel esquerdo e, no painel direito, defina Aplicar restrição de chave estrangeira como 'Sim' (para habilitar restrições de chave estrangeira) ou 'Não' (para desativá-lo). insira a descrição da imagem aqui

AmirHossein Manian
fonte
3

A resposta marcada '905' parece boa, mas não funciona.

A seguir funcionou para mim. Qualquer restrição de chave primária, chave exclusiva ou padrão NÃO pode ser desativada. De fato, se 'sp_helpconstraint' 'mostrar' n / a 'em status_enabled - Significa que NÃO pode ser ativado / desativado.

- Para gerar script para DISABLE

select 'ALTER TABLE ' + object_name(id) + ' NOCHECK CONSTRAINT [' + object_name(constid) + ']'
from sys.sysconstraints 
where status & 0x4813 = 0x813 order by object_name(id)

- Para gerar script para ENABLE

select 'ALTER TABLE ' + object_name(id) + ' CHECK CONSTRAINT [' + object_name(constid) + ']'
from sys.sysconstraints 
where status & 0x4813 = 0x813 order by object_name(id)
V. Agarwal
fonte
3

Você realmente deve poder desabilitar as restrições de chave estrangeira da mesma maneira que desabilita temporariamente outras restrições:

Alter table MyTable nocheck constraint FK_ForeignKeyConstraintName

Apenas certifique-se de desabilitar a restrição na primeira tabela listada no nome da restrição. Por exemplo, se minha restrição de chave estrangeira fosse FK_LocationsEmployeesLocationIdEmployeeId, eu desejaria usar o seguinte:

Alter table Locations nocheck constraint FK_LocationsEmployeesLocationIdEmployeeId

mesmo que violar essa restrição produza um erro que não indica necessariamente essa tabela como a origem do conflito.

lwilliams
fonte
1

Um script para governar todos eles: isso combina comandos truncar e excluir com sp_MSforeachtable, para que você possa evitar soltar e recriar restrições - basta especificar as tabelas que precisam ser excluídas em vez de truncadas e, para meus propósitos, incluí um filtro de esquema extra para o bem medida (testada em 2008r2)

declare @schema nvarchar(max) = 'and Schema_Id=Schema_id(''Value'')'
declare @deletiontables nvarchar(max) = '(''TableA'',''TableB'')'
declare @truncateclause nvarchar(max) = @schema + ' and o.Name not in ' +  + @deletiontables;
declare @deleteclause nvarchar(max) = @schema + ' and o.Name in ' + @deletiontables;        

exec sp_MSforeachtable 'alter table ? nocheck constraint all', @whereand=@schema
exec sp_MSforeachtable 'truncate table ?', @whereand=@truncateclause
exec sp_MSforeachtable 'delete from ?', @whereand=@deleteclause
exec sp_MSforeachtable 'alter table ? with check check constraint all', @whereand=@schema
Alex Hinton
fonte
1

Você pode desativar temporariamente as restrições em suas tabelas, trabalhar e depois reconstruí-las.

Aqui está uma maneira fácil de fazer isso ...

Desative todos os índices, incluindo as chaves primárias, que desativarão todas as chaves estrangeiras e, em seguida, reative apenas as chaves primárias para que você possa trabalhar com elas ...

DECLARE @sql AS NVARCHAR(max)=''
select @sql = @sql +
    'ALTER INDEX ALL ON [' + t.[name] + '] DISABLE;'+CHAR(13)
from  
    sys.tables t
where type='u'

select @sql = @sql +
    'ALTER INDEX ' + i.[name] + ' ON [' + t.[name] + '] REBUILD;'+CHAR(13)
from  
    sys.key_constraints i
join
    sys.tables t on i.parent_object_id=t.object_id
where
    i.type='PK'


exec dbo.sp_executesql @sql;
go

[Faça algo, como carregar dados]

Em seguida, reative e reconstrua os índices ...

DECLARE @sql AS NVARCHAR(max)=''
select @sql = @sql +
    'ALTER INDEX ALL ON [' + t.[name] + '] REBUILD;'+CHAR(13)
from  
    sys.tables t
where type='u'

exec dbo.sp_executesql @sql;
go
Carter Medlin
fonte
Isso parecia promissor, mas @sqlsempre é truncado. :(
Will Strohl
1

Eu tenho uma versão mais útil se você estiver interessado. Tirei um pouco de código daqui de um site em que o link não está mais ativo. Eu o modifiquei para permitir uma matriz de tabelas no procedimento armazenado e ele preenche as instruções de descartar, truncar e adicionar antes de executar todas elas. Isso lhe dá controle para decidir quais tabelas precisam ser truncadas.

/****** Object:  UserDefinedTableType [util].[typ_objects_for_managing]    Script Date: 03/04/2016 16:42:55 ******/
CREATE TYPE [util].[typ_objects_for_managing] AS TABLE(
    [schema] [sysname] NOT NULL,
    [object] [sysname] NOT NULL
)
GO

create procedure [util].[truncate_table_with_constraints]
@objects_for_managing util.typ_objects_for_managing readonly

--@schema sysname
--,@table sysname

as 
--select
--    @table = 'TABLE',
--    @schema = 'SCHEMA'

declare @exec_table as table (ordinal int identity (1,1), statement nvarchar(4000), primary key (ordinal));

--print '/*Drop Foreign Key Statements for ['+@schema+'].['+@table+']*/'

insert into @exec_table (statement)
select
          'ALTER TABLE ['+SCHEMA_NAME(o.schema_id)+'].['+ o.name+'] DROP CONSTRAINT ['+fk.name+']'
from sys.foreign_keys fk
inner join sys.objects o
          on fk.parent_object_id = o.object_id
where 
exists ( 
select * from @objects_for_managing chk 
where 
chk.[schema] = SCHEMA_NAME(o.schema_id)  
and 
chk.[object] = o.name
) 
;
          --o.name = @table and
          --SCHEMA_NAME(o.schema_id)  = @schema

insert into @exec_table (statement) 
select
'TRUNCATE TABLE ' + src.[schema] + '.' + src.[object] 
from @objects_for_managing src
; 

--print '/*Create Foreign Key Statements for ['+@schema+'].['+@table+']*/'
insert into @exec_table (statement)
select 'ALTER TABLE ['+SCHEMA_NAME(o.schema_id)+'].['+o.name+'] ADD CONSTRAINT ['+fk.name+'] FOREIGN KEY (['+c.name+']) 
REFERENCES ['+SCHEMA_NAME(refob.schema_id)+'].['+refob.name+'](['+refcol.name+'])'
from sys.foreign_key_columns fkc
inner join sys.foreign_keys fk
          on fkc.constraint_object_id = fk.object_id
inner join sys.objects o
          on fk.parent_object_id = o.object_id
inner join sys.columns c
          on      fkc.parent_column_id = c.column_id and
                   o.object_id = c.object_id
inner join sys.objects refob
          on fkc.referenced_object_id = refob.object_id
inner join sys.columns refcol
          on fkc.referenced_column_id = refcol.column_id and
                   fkc.referenced_object_id = refcol.object_id
where 
exists ( 
select * from @objects_for_managing chk 
where 
chk.[schema] = SCHEMA_NAME(o.schema_id)  
and 
chk.[object] = o.name
) 
;

          --o.name = @table and
          --SCHEMA_NAME(o.schema_id)  = @schema



declare @looper int , @total_records int, @sql_exec nvarchar(4000)

select @looper = 1, @total_records = count(*) from @exec_table; 

while @looper <= @total_records 
begin

select @sql_exec = (select statement from @exec_table where ordinal =@looper)
exec sp_executesql @sql_exec 
print @sql_exec 
set @looper = @looper + 1
end
Zak Willis
fonte
Link morto na sua resposta. Aponta para um artigo em branco.
Neolisk
Olá, pode haver um link morto, mas todo o código está especificado na peça. O que está errado com isto?
Zak Willis
Não há nada errado, mas você provavelmente deve editar sua resposta e remover o link morto.
Neolisk 9/01/19