Comment les contraintes de clé étrangère peuvent-elles être temporairement désactivées à l'aide de T-SQL?

824

La désactivation et l'activation des contraintes de clé étrangère sont-elles prises en charge dans SQL Server? Ou est-ce que ma seule option est de supprimerdrop ensuite les contraintes?create

Rayon
la source
128
Pour ceux qui demandent "pourquoi", je voudrais faire ceci: c'est pour un environnement de test où je veux pouvoir supprimer et charger des données de test de plusieurs tables sans avoir besoin de maintenir et de spécifier l'ordre de chargement des données. L'intégrité des données n'est pas si importante pour ce scénario.
Ray
8
Remarque - si vous souhaitez TRONCER la table, vous devrez supprimer les contraintes.
OutstandingBill
@OutstandingBill Apparemment, cela fonctionne pour TRUNCATE .
jpaugh
1
Il semble étrange que quiconque le remette en question dans un environnement de production . Un cas d'utilisation très courant concerne les inserts en vrac. Si vous avez une table auto-référentielle, il est parfois extrêmement difficile de trier une insertion en bloc afin que la ligne parent soit toujours insérée avant l'enfant, vous désactivez donc la contrainte, l'insertion en bloc, activez la contrainte.
Auspex

Réponses:

1126

Si vous souhaitez désactiver toutes les contraintes de la base de données, exécutez simplement ce code:

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

Pour les réactiver, exécutez: (l'impression est facultative bien sûr et il s'agit simplement de lister les tableaux)

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

Je trouve cela utile lors du remplissage des données d'une base de données à une autre. C'est une bien meilleure approche que de supprimer des contraintes. Comme vous l'avez mentionné, il est utile lors de la suppression de toutes les données dans la base de données et de son repeuplement (par exemple dans un environnement de test).

Si vous supprimez toutes les données, cette solution peut vous être utile.

Parfois, il est également pratique de désactiver tous les déclencheurs, vous pouvez voir la solution complète ici .

kristof
la source
9
"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"Doit-il y avoir un seul "CHECK"?
CrazyPyro
27
@CrazyPyro - non, vous avez besoin des deux
kristof
32
@CrazyPyro: les deux sont en effet nécessaires, la raison en est que le premier CHECK appartient au WITH et le second CHECK au CONSTRAINT (c'est le type de contrainte). Le premier CHECK garantit la cohérence de vos données lors de l'activation de la contrainte. Si vous ne le souhaitez pas, vous pouvez écrire AVEC NOCHECK. Peut être utile dans certaines situations de test lorsque vous ne vous souciez pas des données réelles, tant qu'il y en a pour que vos requêtes aient quelque chose à jouer.
Valentino Vranken
8
Est-ce mauvais que j'obtienne ce résultat sur la deuxième commande? "L'instruction ALTER TABLE était en conflit avec la contrainte FOREIGN KEY ..."
Devil's Advocate
34
Il convient de noter que même avec des contraintes désactivées, TRUNCATE TABLE ne fonctionnera pas. Pour cela, vous devrez supprimer les contraintes. Sinon, utilisez DELETE FROM, mais prenez en considération la différence: 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
la source
29
une bonne trouvaille, mais notez que vous ne pouvez toujours pas tronquer le tableau sans supprimer les contraintes de clé étrangère
Steven A. Lowe
1
et vous devrez également savoir que lorsque vous réactivez les contraintes et effectuez une vérification de l'intégrité des données, vos données peuvent échouer et résoudre un problème comme celui-ci peut être un cauchemar si les données défaillantes se trouvent à la fin d'une longue chaîne de contraintes liées.
Jimoc
1
Vous avez également besoin d'une deuxième vérification lorsque vous réactivez les contraintes. Sinon, en l'état, votre code ne vérifiera la contrainte qu'une seule fois, pas l'activer.
ps2goat
3
oui, "avec chèque" nécessaire pour 2012. La modification a été refusée? MS Link
crokusek
1
Les instructions utilisées ici pour réactiver les contraintes ne contiennent pas les clauses WITH CHECK. C'est une faille assez importante. Voir ma réponse pour plus de détails - stackoverflow.com/a/35427150/81595 .
Scott Munro
37

Pour désactiver la contrainte, vous avez ALTERla table à l'aide de NOCHECK

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

Pour vous permettre d'avoir à utiliser le double CHECK :

ALTER TABLE [TABLE_NAME] WITH CHECK CHECK CONSTRAINT [ALL|CONSTRAINT_NAME]
  • Faites attention au double CHECK CHECK lors de l'activation.
  • ALL signifie pour toutes les contraintes du tableau.

Une fois terminé, si vous devez vérifier l'état, utilisez ce script pour répertorier l'état de la contrainte. Sera très utile:

    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
la source
Pas voir pour clés primaires ? Pour les clés étrangères SYSFOREIGNKEYS System View sys.sysforeignkeys msdn.microsoft.com/en-us/library/ms177604.aspx
Kiquenet
si vous essayez de désactiver la clé primaire pour l'insertion, je recommanderais d'utiliser (SET IDENTITY_INSERT) si vous voulez simplement vérifier la clé primaire, vous pouvez essayer sys.key_constraints avec sys.indexes.is_primary_key
Diego Mendes
29

Votre meilleure option consiste à supprimer et créer des contraintes de clé étrangère.

Je n'ai pas trouvé d'exemples dans ce post qui fonctionneraient pour moi "tels quels", l'un ne fonctionnerait pas si les clés étrangères référençaient différents schémas, l'autre ne fonctionnerait pas si la clé étrangère faisait référence à plusieurs colonnes. Ce script prend en compte à la fois plusieurs schémas et plusieurs colonnes par clé étrangère.

Voici le script qui génère les instructions "ADD CONSTRAINT", pour plusieurs colonnes il les séparera par une virgule ( assurez-vous de sauvegarder cette sortie avant d'exécuter les instructions 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

Voici le script qui génère les instructions "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
la source
5
Pouvez-vous expliquer pourquoi c'est mieux que de désactiver et de réactiver les contraintes?
Mahmoodvcs
Joli script. Pour une approche similaire mais alternative, voir: mssqltips.com/sqlservertip/3347/…
Matt Browne
11

La norme SQL-92 permet de déclarer une contrainte comme DEFERRABLE afin qu'elle puisse être différée (implicitement ou explicitement) dans le cadre d'une transaction. Malheureusement, SQL Server manque toujours cette fonctionnalité SQL-92.

Pour moi, changer une contrainte en NOCHECK revient à changer la structure de la base de données à la volée - la suppression des contraintes l'est certainement - et quelque chose à éviter (par exemple, les utilisateurs ont besoin de privilèges accrus).

un jour
la source
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

Je suis d'accord avec toi, Hamlin. Lorsque vous transférez des données à l'aide de SSIS ou lorsque vous souhaitez répliquer des données, il semble tout à fait nécessaire de désactiver ou de supprimer temporairement les contraintes de clé étrangère, puis de les réactiver ou de les recréer. Dans ces cas, l'intégrité référentielle n'est pas un problème, car elle est déjà conservée dans la base de données source. Par conséquent, vous pouvez être assuré à ce sujet.

Amir Hussein Samiani
la source
Ce script est idéal pour générer mes commandes "ALTER", mais comment les faire exécuter / exécuter dans un SP?
BlueChippy
1
Je pense que cela ne fonctionnera pas si l'une des clés étrangères est multi-colonne
Zar Shardan
Cela n'a pas non plus produit tous les caractères pour les noms de table / clé trop longs.
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
la source
10

WITH CHECK CHECK est presque certainement nécessaire!

Ce point a été soulevé dans certaines des réponses et des commentaires, mais je pense qu'il est assez important de le rappeler.

Réactiver une contrainte à l'aide de la commande suivante (non WITH CHECK ) aura de sérieux inconvénients .

ALTER TABLE MyTable CHECK CONSTRAINT MyConstraint;

AVEC CHÈQUE | AVEC NOCHECK

Spécifie si les données de la table sont ou non validées par rapport à une contrainte FOREIGN KEY ou CHECK nouvellement ajoutée ou réactivée. S'il n'est pas spécifié, WITH CHECK est supposé pour les nouvelles contraintes et WITH NOCHECK est supposé pour les contraintes réactivées.

Si vous ne souhaitez pas vérifier les nouvelles contraintes CHECK ou FOREIGN KEY par rapport aux données existantes, utilisez WITH NOCHECK. Nous vous déconseillons de le faire, sauf dans de rares cas. La nouvelle contrainte sera évaluée dans toutes les mises à jour de données ultérieures. Toute violation de contrainte supprimée par WITH NOCHECK lors de l'ajout de la contrainte peut entraîner l'échec des futures mises à jour si elles mettent à jour des lignes avec des données non conformes à la contrainte.

L'optimiseur de requêtes ne prend pas en compte les contraintes définies AVEC NOCHECK. Ces contraintes sont ignorées jusqu'à ce qu'elles soient réactivées à l'aide de la table ALTER TABLE AVEC CHECK CHECK CONSTRAINT ALL.

Remarque: WITH NOCHECK est la valeur par défaut pour la réactivation des contraintes. Je dois me demander pourquoi ...

  1. Aucune donnée existante dans la table ne sera évaluée pendant l'exécution de cette commande - une exécution réussie ne garantit pas que les données de la table sont valides selon la contrainte.
  2. Lors de la prochaine mise à jour des enregistrements non valides, la contrainte sera évaluée et échouera, ce qui entraînera des erreurs qui peuvent ne pas être liées à la mise à jour réelle effectuée.
  3. La logique d'application qui s'appuie sur la contrainte pour garantir la validité des données peut échouer.
  4. L'optimiseur de requêtes n'utilisera aucune contrainte activée de cette manière.

La vue système sys.foreign_keys offre une certaine visibilité sur le problème. Notez qu'il a à la fois un is_disabledet une is_not_trustedcolonne. is_disabledindique si les futures opérations de manipulation de données seront validées par rapport à la contrainte. is_not_trustedindique si toutes les données actuellement dans le tableau ont été validées par rapport à la contrainte.

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint;

Faut-il faire confiance à vos contraintes? Trouver...

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

Premier poste :)

Pour l'OP, la solution de kristof fonctionnera, sauf s'il y a des problèmes avec des données massives et des problèmes de ballon de journal des transactions sur les grandes suppressions. De plus, même avec le stockage de tlog à épargner, car les suppressions d'écriture dans le tlog, l'opération peut prendre TRÈS longtemps pour les tables avec des centaines de millions de lignes.

J'utilise une série de curseurs pour tronquer et recharger fréquemment de grandes copies de l'une de nos énormes bases de données de production. La solution conçue tient compte de plusieurs schémas, de plusieurs colonnes de clés étrangères et, mieux encore, peut être lancée pour être utilisée dans SSIS.

Cela implique la création de trois tables intermédiaires (tables réelles) pour héberger les scripts DROP, CREATE et CHECK FK, la création et l'insertion de ces scripts dans les tables, puis la boucle sur les tables et leur exécution. Le script joint se compose de quatre parties: 1.) création et stockage des scripts dans les trois tables intermédiaires (réelles), 2.) exécution des scripts drop FK via un curseur un par un, 3.) Utilisation de sp_MSforeachtable pour tronquer tous les tables de la base de données autres que nos trois tables intermédiaires et 4.) exécution de la création de FK et vérification des scripts FK à la fin de votre package ETL SSIS.

Exécutez la partie de création de script dans une tâche d'exécution SQL dans SSIS. Exécutez la partie "Exécuter Drop FK Scripts" dans une deuxième tâche d'exécution SQL. Placez le script de troncature dans une troisième tâche d'exécution SQL, puis effectuez les autres processus ETL que vous devez faire avant d'attacher les scripts CREATE et CHECK dans une dernière tâche d'exécution SQL (ou deux si vous le souhaitez) à la fin de votre flux de contrôle.

Le stockage des scripts dans de vraies tables s'est avéré inestimable lorsque la réapplication des clés étrangères échoue car vous pouvez sélectionner * à partir de sync_CreateFK, copier / coller dans votre fenêtre de requête, les exécuter un à la fois et résoudre les problèmes de données une fois que vous trouver ceux qui ont échoué / ne parviennent toujours pas à réappliquer.

Ne réexécutez pas le script s'il échoue sans vous assurer que vous réappliquez toutes les clés / vérifications étrangères avant de le faire, ou vous perdrez très probablement une création et vérifier le script fk que nos tables de transfert sont supprimées et recréé avant la création des scripts à exécuter.

----------------------------------------------------------------------------
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
la source
7

Trouver la contrainte

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

Exécuter le SQL généré par ce 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')

Safeway.

Remarque: Ajout d'une solution pour supprimer la contrainte afin que la table puisse être supprimée ou modifiée sans aucune erreur de contrainte.

Aditya
la source
3

Cliquez avec le bouton droit sur la conception de la table et accédez à Relations et choisissez la clé étrangère dans le volet gauche et dans le volet droit, définissez Appliquer la contrainte de clé étrangère sur 'Oui' (pour activer les contraintes de clé étrangère) ou 'Non' (pour désactiver). entrez la description de l'image ici

AmirHossein Manian
la source
3

La réponse marquée «905» semble bonne mais ne fonctionne pas.

La suite a fonctionné pour moi. Aucune clé primaire, clé unique ou contrainte par défaut NE PEUT être désactivée. En fait, si 'sp_helpconstraint' 'affiche' n / a 'dans status_enabled - Cela signifie qu'il ne peut PAS être activé / désactivé.

- Pour générer un script pour désactiver

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

- Pour générer un script pour ACTIVER

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
la source
3

Vous devriez pouvoir désactiver les contraintes de clé étrangère de la même manière que vous désactivez temporairement les autres contraintes:

Alter table MyTable nocheck constraint FK_ForeignKeyConstraintName

Assurez-vous simplement de désactiver la contrainte sur la première table répertoriée dans le nom de la contrainte. Par exemple, si ma contrainte de clé étrangère était FK_LocationsEmployeesLocationIdEmployeeId, je voudrais utiliser ce qui suit:

Alter table Locations nocheck constraint FK_LocationsEmployeesLocationIdEmployeeId

même si la violation de cette contrainte produira une erreur qui n'indiquera pas nécessairement cette table comme source du conflit.

lwilliams
la source
1

Un script pour les gouverner tous: il combine les commandes tronquer et supprimer avec sp_MSforeachtable afin que vous puissiez éviter de supprimer et de recréer des contraintes - spécifiez simplement les tables qui doivent être supprimées plutôt que tronquées et pour mes besoins, j'ai inclus un filtre de schéma supplémentaire pour de bon mesure (testé en 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
la source
1

Vous pouvez désactiver temporairement les contraintes sur vos tables, travailler, puis les reconstruire.

Voici un moyen facile de le faire ...

Désactivez tous les index, y compris les clés primaires, ce qui désactivera toutes les clés étrangères, puis réactivez uniquement les clés primaires pour pouvoir travailler avec elles ...

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

[Faites quelque chose, comme charger des données]

Puis réactivez et reconstruisez les index ...

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
la source
Cela semblait prometteur, mais @sqltoujours tronqué. :(
Will Strohl
1

J'ai une version plus utile si vous êtes intéressé. J'ai retiré un peu de code d'ici un site Web où le lien n'est plus actif. Je l'ai modifié pour permettre un tableau de tables dans la procédure stockée et il remplit les instructions drop, truncate, add avant de les exécuter toutes. Cela vous donne le contrôle pour décider quelles tables doivent être tronquées.

/****** 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
la source
Lien mort dans votre réponse. Pointe vers un article vierge.
Neolisk
Salut, il peut bien y avoir un lien mort mais tout le code est spécifié dans la pièce. Quel est le probleme avec ca?
Zak Willis
Rien de mal, mais vous devriez probablement modifier votre réponse et supprimer le lien mort.
Neolisk