Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Desative temporariamente todas as restrições de chave estrangeira


Para desabilitar restrições de chave estrangeira:
DECLARE @sql NVARCHAR(MAX) = N'';

;WITH x AS 
(
  SELECT DISTINCT obj = 
      QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id)) + '.' 
    + QUOTENAME(OBJECT_NAME(parent_object_id)) 
  FROM sys.foreign_keys
)
SELECT @sql += N'ALTER TABLE ' + obj + ' NOCHECK CONSTRAINT ALL;
' FROM x;

EXEC sp_executesql @sql;

Para reativar:
DECLARE @sql NVARCHAR(MAX) = N'';

;WITH x AS 
(
  SELECT DISTINCT obj = 
      QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id)) + '.' 
    + QUOTENAME(OBJECT_NAME(parent_object_id)) 
  FROM sys.foreign_keys
)
SELECT @sql += N'ALTER TABLE ' + obj + ' WITH CHECK CHECK CONSTRAINT ALL;
' FROM x;

EXEC sp_executesql @sql;

No entanto, você não poderá truncar as tabelas, você terá que excluí-las na ordem correta. Se você precisar truncar eles, você precisa eliminar completamente as restrições e recriá-las. Isso é simples de fazer se suas restrições de chave estrangeira forem todas simples, restrições de coluna única, mas definitivamente mais complexa se houver várias colunas envolvidas.

Aqui está algo que você pode tentar. Para tornar isso parte do seu pacote SSIS, você precisará de um local para armazenar as definições FK enquanto o pacote SSIS é executado (você não poderá fazer tudo isso em um script). Então, em algum banco de dados utilitário, crie uma tabela:
CREATE TABLE dbo.PostCommand(cmd NVARCHAR(MAX));

Então, em seu banco de dados, você pode ter um procedimento armazenado que faz isso:
DELETE other_database.dbo.PostCommand;

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(fk.parent_object_id))
   + '.' + QUOTENAME(OBJECT_NAME(fk.parent_object_id)) 
   + ' ADD CONSTRAINT ' + fk.name + ' FOREIGN KEY (' 
   + STUFF((SELECT ',' + c.name
    FROM sys.columns AS c 
        INNER JOIN sys.foreign_key_columns AS fkc 
        ON fkc.parent_column_id = c.column_id
        AND fkc.parent_object_id = c.[object_id]
    WHERE fkc.constraint_object_id = fk.[object_id]
    ORDER BY fkc.constraint_column_id 
    FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, '')
+ ') REFERENCES ' + 
QUOTENAME(OBJECT_SCHEMA_NAME(fk.referenced_object_id))
+ '.' + QUOTENAME(OBJECT_NAME(fk.referenced_object_id))
+ '(' + 
STUFF((SELECT ',' + c.name
    FROM sys.columns AS c 
        INNER JOIN sys.foreign_key_columns AS fkc 
        ON fkc.referenced_column_id = c.column_id
        AND fkc.referenced_object_id = c.[object_id]
    WHERE fkc.constraint_object_id = fk.[object_id]
    ORDER BY fkc.constraint_column_id 
    FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, '') + ');
' FROM sys.foreign_keys AS fk
WHERE OBJECTPROPERTY(parent_object_id, 'IsMsShipped') = 0;

INSERT other_database.dbo.PostCommand(cmd) SELECT @sql;

IF @@ROWCOUNT = 1
BEGIN
  SET @sql = N'';

  SELECT @sql += N'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(fk.parent_object_id))
    + '.' + QUOTENAME(OBJECT_NAME(fk.parent_object_id)) 
    + ' DROP CONSTRAINT ' + fk.name + ';
  ' FROM sys.foreign_keys AS fk;

  EXEC sp_executesql @sql;
END

Agora, quando seu pacote SSIS estiver concluído, ele deverá chamar um procedimento armazenado diferente, que faz:
DECLARE @sql NVARCHAR(MAX);

SELECT @sql = cmd FROM other_database.dbo.PostCommand;

EXEC sp_executesql @sql;

Se você está fazendo tudo isso apenas para poder truncar em vez de excluir, sugiro apenas pegar o hit e executar uma exclusão. Talvez use o modelo de recuperação bulk-logged para minimizar o impacto do log. Em geral, não vejo como essa solução será muito mais rápida do que apenas usar uma exclusão na ordem correta.

Em 2014 publiquei um post mais elaborado sobre isso aqui:
  • Elimine e recrie todas as restrições de chave estrangeira no SQL Server