web-dev-qa-db-fra.com

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

J'exécute un package SSIS qui remplacera les données de quelques tables de FlatFiles aux tables existantes d'une base de données.

Mon paquet tronquera les tables puis insérera les nouvelles données. Lorsque j'exécute mon package SSIS, je reçois une exception à cause des clés étrangères.

Puis-je désactiver les contraintes, lancer mon importation, puis les réactiver?

41
HaBo

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 les tronquer, vous devez supprimer complètement les contraintes et les recréer. Cette opération est simple si vos contraintes de clé étrangère sont toutes simples, à une seule colonne, mais nettement 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 effectuer tout cela dans un script). Donc, dans une base de données d’utilitaires, 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 effectue 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

Désormais, lorsque votre package SSIS est terminé, il doit appeler une autre procédure stockée, qui:

DECLARE @sql NVARCHAR(MAX);

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

EXEC sp_executesql @sql;

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

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

77
Aaron Bertrand

Utilisez la procédure intégrée sp_msforeachtable .

Pour désactiver toutes les contraintes:

EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT ALL";

Pour activer toutes les contraintes:

EXEC sp_msforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL";

Pour supprimer toutes les tables:

EXEC sp_msforeachtable "DROP TABLE ?";
29
Ed Randall

Une bonne référence est donnée à: http://msdn.Microsoft.com/en-us/magazine/cc163442.aspx Dans la section "Désactivation de toutes les clés étrangères".

En s'inspirant de cela, une approche peut être réalisée en créant une table temporaire et en insérant les contraintes dans cette table, puis en supprimant les contraintes et en les réappliquant à partir de cette table temporaire. Assez dit voici ce que je parle

 SET NOCOUNT ON

    DECLARE @temptable TABLE(
       Id INT PRIMARY KEY IDENTITY(1, 1),
       FKConstraintName VARCHAR(255),
       FKConstraintTableSchema VARCHAR(255),
       FKConstraintTableName VARCHAR(255),
       FKConstraintColumnName VARCHAR(255),
       PKConstraintName VARCHAR(255),
       PKConstraintTableSchema VARCHAR(255),
       PKConstraintTableName VARCHAR(255),
       PKConstraintColumnName VARCHAR(255)    
    )

    INSERT INTO @temptable(FKConstraintName, FKConstraintTableSchema, FKConstraintTableName, FKConstraintColumnName)
    SELECT 
       KeyColumnUsage.CONSTRAINT_NAME, 
       KeyColumnUsage.TABLE_SCHEMA, 
       KeyColumnUsage.TABLE_NAME, 
       KeyColumnUsage.COLUMN_NAME 
    FROM 
       INFORMATION_SCHEMA.KEY_COLUMN_USAGE KeyColumnUsage
          INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TableConstraints
             ON KeyColumnUsage.CONSTRAINT_NAME = TableConstraints.CONSTRAINT_NAME
    WHERE
       TableConstraints.CONSTRAINT_TYPE = 'FOREIGN KEY'

    UPDATE @temptable SET
       PKConstraintName = UNIQUE_CONSTRAINT_NAME
    FROM 
       @temptable tt
          INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ReferentialConstraint
             ON tt.FKConstraintName = ReferentialConstraint.CONSTRAINT_NAME

    UPDATE @temptable SET
       PKConstraintTableSchema  = TABLE_SCHEMA,
       PKConstraintTableName  = TABLE_NAME
    FROM @temptable tt
       INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TableConstraints
          ON tt.PKConstraintName = TableConstraints.CONSTRAINT_NAME

    UPDATE @temptable SET
       PKConstraintColumnName = COLUMN_NAME
    FROM @temptable tt
       INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KeyColumnUsage
          ON tt.PKConstraintName = KeyColumnUsage.CONSTRAINT_NAME


    --Now to drop constraint:
    SELECT
       '
       ALTER TABLE [' + FKConstraintTableSchema + '].[' + FKConstraintTableName + '] 
       DROP CONSTRAINT ' + FKConstraintName + '

       GO'
    FROM
       @temptable

    --Finally to add constraint:
    SELECT
       '
       ALTER TABLE [' + FKConstraintTableSchema + '].[' + FKConstraintTableName + '] 
       ADD CONSTRAINT ' + FKConstraintName + ' FOREIGN KEY(' + FKConstraintColumnName + ') REFERENCES [' + PKConstraintTableSchema + '].[' + PKConstraintTableName + '](' + PKConstraintColumnName + ')

       GO'
    FROM
       @temptable

    GO
3
NG.

Désactiver toutes les contraintes de table

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName

- Activer toutes les contraintes de table

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
2
Alam Usmani

Tronquer la table ne sera pas possible même si vous désactivez les clés étrangères.Vous pouvez également utiliser la commande Delete pour supprimer tous les enregistrements de la table, mais sachez que vous utilisez la commande delete Pour une table composée de des millions d’enregistrements, votre paquet sera lent .__ et la taille de votre journal des transactions augmentera, ce qui risque de saturer votre précieux espace disque.

Si vous supprimez les contraintes, il se peut que vous remplissiez votre table avec des données impures Si vous essayez de les recréer, elles risquent de ne pas vous permettre, car cela vous donnera des erreurs . Alors assurez-vous Supprimez les contraintes, vous chargez des données qui sont correctement liées les unes aux autres et satisfont les relations de contrainte que vous allez recréer.

alors réfléchissez bien aux avantages et inconvénients de chaque méthode et utilisez-la selon vos besoins.

1
vimal vasudevan

pas besoin d'exécuter des requêtes à FK sidable sur SQL. Si vous avez un FK des tables A à B, vous devriez:

  • supprimer les données de la table A 
  • supprimer les données de la table B
  • insérer des données sur B
  • insérer des données sur A

Vous pouvez également dire à la destination de ne pas vérifier les contraintes

enter image description here

1
Diego

Si vous utilisez un schéma de base de données différent de ".dbo" ou si votre base contient des Pk, composés de plusieurs champs, veuillez ne pas utiliser la solution de Carter Medlin, sinon vous endommageriez votre base !!!

Lorsque vous travaillez avec différents schémas, essayez ceci (n’oubliez pas de faire une sauvegarde de votre base de données auparavant!):

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

select @sql = @sql +
    'ALTER INDEX ' + i.[name] + ' ON ' + SCHEMA_NAME( t.schema_id) +'.'+'[' + 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

Après avoir effectué certaines actions sans Fk, vous pouvez revenir en arrière avec

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

exec dbo.sp_executesql @sql;
exec sp_msforeachtable "ALTER TABLE ? WITH NOCHECK CHECK CONSTRAINT ALL";
0
olaf870