Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Désactiver temporairement toutes les contraintes de clé étrangère

Pour désactiver les contraintes de clé étrangère :

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;

Pour réactiver :

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;

Cependant, vous ne pourrez pas tronquer les tables, vous devrez les supprimer dans le bon ordre. Si vous devez tronquer eux, vous devez supprimer entièrement les contraintes et les recréer. C'est simple à faire si vos contraintes de clé étrangère sont toutes des contraintes simples à une seule colonne, mais certainement plus complexes si plusieurs colonnes sont impliquées.

Voici quelque chose que vous pouvez essayer. Afin d'en faire une partie de votre package SSIS, vous aurez besoin d'un emplacement pour stocker les définitions FK pendant l'exécution du package SSIS (vous ne pourrez pas tout faire dans un seul script). Donc, dans une base de données utilitaire, créez une table :

CREATE TABLE dbo.PostCommand(cmd NVARCHAR(MAX));

Ensuite, dans votre base de données, vous pouvez avoir une procédure stockée qui fait ceci :

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

Maintenant, lorsque votre package SSIS est terminé, il doit appeler une procédure stockée différente, qui :

DECLARE @sql NVARCHAR(MAX);

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

EXEC sp_executesql @sql;

Si vous faites tout cela juste pour pouvoir tronquer au lieu de supprimer, je suggère simplement de prendre le coup et d'exécuter une suppression. Peut-être utiliser un modèle de récupération en masse pour minimiser l'impact du journal. En général, je ne vois pas en quoi cette solution sera beaucoup plus rapide que d'utiliser simplement une suppression dans le bon ordre.

En 2014, j'ai publié un article plus élaboré à ce sujet ici :

  • Supprimer et recréer toutes les contraintes de clé étrangère dans SQL Server