Come è possibile disabilitare temporaneamente i vincoli di chiave esterna utilizzando T-SQL?


824

La disabilitazione e l'abilitazione dei vincoli di chiave esterna sono supportate in SQL Server? O è la mia unica opzione per drope quindi rielaborarecreate i vincoli?


128
Per le persone che chiedono "perché" vorrei fare questo: è per un ambiente di test in cui voglio poter rimuovere e caricare i dati di test da più tabelle senza la necessità di mantenere e specificare l'ordine di caricamento dei dati. L'integrità dei dati non è così importante per questo scenario.
Ray

8
Nota: se si desidera TRUNCARE la tabella, è necessario eliminare effettivamente i vincoli.
Eccezionale: conto

@OutstandingBill Apparentemente, questo funziona per TRUNCATE .
jpaugh

1
Sembra strano che qualcuno possa metterlo in discussione in un ambiente di produzione . Un caso d'uso molto comune è per inserti di grandi dimensioni. Se si dispone di una tabella autoreferenziale, a volte è estremamente difficile ordinare un inserimento in blocco in modo che la riga padre sia sempre inserita prima del figlio, quindi si disabilita il vincolo, l'inserimento in blocco, si abilita il vincolo.
Auspex,

Risposte:


1126

Se si desidera disabilitare tutti i vincoli nel database, eseguire semplicemente questo codice:

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

Per riaccenderli, esegui: (la stampa è facoltativa ovviamente e sta semplicemente elencando le tabelle)

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

Lo trovo utile quando si popolano i dati da un database all'altro. È un approccio molto migliore rispetto alla caduta di vincoli. Come accennato, risulta utile quando si eliminano tutti i dati nel database e li si ripopola (ad esempio nell'ambiente di test).

Se si eliminano tutti i dati, è possibile che questa soluzione sia utile.

Inoltre a volte è utile disabilitare tutti i trigger, puoi vedere la soluzione completa qui .


9
"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"Dovrebbe esserci un solo "CONTROLLO" lì?
CrazyPyro,

27
@CrazyPyro - no hai bisogno di entrambi
kristof

32
@CrazyPyro: entrambi sono effettivamente necessari, motivo per cui il primo CHECK appartiene al WITH e il secondo CHECK con il Vincolo (è il tipo di vincolo). Il primo CHECK assicura che i dati vengano verificati per coerenza quando si attiva il vincolo. Se non lo desideri, puoi scrivere CON NOCHECK. Può essere utile in determinate situazioni di test quando non ti interessano i dati reali, purché ce ne siano alcuni in modo che le tue domande abbiano qualcosa con cui giocare.
Valentino Vranken,

8
È male che ottengo questo risultato sul secondo comando? "L'istruzione ALTER TABLE è in conflitto con il vincolo FOREIGN KEY ..."
Devil's Advocate,

34
Vale la pena notare che anche con i vincoli disabilitati, TRUNCATE TABLE non funzionerà. Per questo, dovrai eliminare i vincoli. Altrimenti, usa DELETE FROM, ma prendi in considerazione la differenza: 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

29
una buona scoperta, ma nota che non puoi ancora troncare la tabella senza rimuovere i vincoli di chiave esterna
Steven A. Lowe,

1
e dovrai anche essere consapevole che quando riattivi i vincoli ed esegui un controllo di integrità dei dati, i tuoi dati potrebbero non riuscire e risolvere un problema del genere può essere un incubo se i dati in errore si trovano alla fine di una lunga serie di vincoli collegati.
Jimoc,

1
È inoltre necessario un secondo controllo quando si riattivano i vincoli. Altrimenti, così com'è, il tuo codice controllerà il vincolo solo una volta, non lo accenderà.
ps2goat,

3
sì, "con check check" necessario per il 2012. La modifica è stata respinta? MS Link
crokusek,

1
Nelle istruzioni utilizzate qui per riattivare i vincoli mancano le clausole WITH CHECK. Questo è un grosso difetto. Vedi la mia risposta per i dettagli - stackoverflow.com/a/35427150/81595 .
Scott Munro,

37

Per disabilitare il vincolo hai ALTERla tabella usando NOCHECK

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

Per consentire all'utente di utilizzare doppio CONTROLLO :

ALTER TABLE [TABLE_NAME] WITH CHECK CHECK CONSTRAINT [ALL|CONSTRAINT_NAME]
  • Prestare attenzione al doppio CHECK CHECK al momento dell'abilitazione.
  • TUTTI significa per tutti i vincoli nella tabella.

Una volta completato, se è necessario controllare lo stato, utilizzare questo script per elencare lo stato del vincolo. Sarà molto 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 

Non visualizzare per chiavi primarie ? Per chiavi esterne SYSFOREIGNKEYS System View sys.sysforeignkeys msdn.microsoft.com/en-us/library/ms177604.aspx~~V~~singular~~3rd
Kiquenet

se stai cercando di disabilitare la chiave primaria per l'inserimento, consiglierei di usare (SET IDENTITY_INSERT) se vuoi solo controllare la chiave primaria, puoi provare sys.key_constraints con sys.indexes.is_primary_key
Diego Mendes

29

L'opzione migliore è GOCCARE e CREARE vincoli di chiave esterna.

Non ho trovato esempi in questo post che funzionerebbero per me "così come sono", uno non funzionerebbe se le chiavi esterne fanno riferimento a schemi diversi, l'altro non funzionerebbe se la chiave esterna fa riferimento a più colonne. Questo script considera sia schemi multipli che colonne multiple per chiave esterna.

Ecco lo script che genera istruzioni "AGGIUNGI VINCITORE", per più colonne le separerà con una virgola ( assicurarsi di salvare questo output prima di eseguire le istruzioni 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

Ecco lo script che genera le dichiarazioni "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

5
Puoi spiegare perché è meglio disabilitare e riattivare i vincoli?
Mahmoodvcs,

Bel copione. Per un approccio simile ma alternativo, consultare: mssqltips.com/sqlservertip/3347/…
Matt Browne,

11

Lo standard SQL-92 consente di dichiarare un vincolo DEFERRABLE in modo che possa essere rinviato (implicitamente o esplicitamente) nell'ambito di una transazione. Purtroppo, a SQL Server manca ancora questa funzionalità SQL-92.

Per me, cambiare un vincolo in NOCHECK è come cambiare la struttura del database al volo - certamente vincoli di caduta - e qualcosa da evitare (ad esempio, gli utenti richiedono privilegi più elevati).


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

Sono d'accordo con te, Hamlin. Quando si trasferiscono dati utilizzando SSIS o quando si desidera replicare i dati, sembra del tutto necessario disabilitare o eliminare temporaneamente i vincoli di chiave esterna e quindi riattivarli o ricrearli. In questi casi, l'integrità referenziale non è un problema, poiché è già gestita nel database di origine. Pertanto, puoi stare certo su questa questione.


Questo script è ottimo per generare i miei comandi "ALTER", ma come posso farli eseguire / eseguire in un SP?
BlueChippy,

1
Penso che questo non funzionerà se una delle chiavi esterne è multi-colonna
Zar Shardan,

Anche questo non ha prodotto tutti i caratteri per nomi di tabella / chiave estremamente lunghi.
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

10

WITH CHECK CHECK è quasi certamente richiesto!

Questo punto è stato sollevato in alcune delle risposte e dei commenti, ma ritengo che sia abbastanza importante richiamarlo nuovamente.

La riattivazione di un vincolo mediante il comando seguente (no WITH CHECK) presenta alcuni gravi inconvenienti .

ALTER TABLE MyTable CHECK CONSTRAINT MyConstraint;

CON CONTROLLO | CON NOCHECK

Specifica se i dati nella tabella sono o meno convalidati rispetto a un vincolo FOREIGN KEY o CHECK appena aggiunto o riattivato. Se non specificato, WITH CHECK viene assunto per i nuovi vincoli e WITH NOCHECK viene assunto per i vincoli riattivati.

Se non si desidera verificare nuovi vincoli CHECK o FOREIGN KEY rispetto ai dati esistenti, utilizzare WITH NOCHECK. Non è consigliabile farlo, tranne in rari casi. Il nuovo vincolo verrà valutato in tutti gli aggiornamenti successivi dei dati. Qualsiasi violazione del vincolo che viene soppressa da WITH NOCHECK quando viene aggiunto il vincolo può causare errori futuri in caso di aggiornamento di righe con dati non conformi al vincolo.

Query Optimizer non considera i vincoli definiti WITH NOCHECK. Tali vincoli vengono ignorati fino a quando non vengono riattivati ​​utilizzando la tabella ALTER TABLE WITH CHECK CHECK CONSTRAINT ALL.

Nota: WITH NOCHECK è l'impostazione predefinita per riattivare i vincoli. Mi chiedo perché ...

  1. Nessun dato esistente nella tabella verrà valutato durante l'esecuzione di questo comando - il completamento con esito positivo non garantisce che i dati nella tabella siano validi in base al vincolo.
  2. Durante il prossimo aggiornamento dei record non validi, il vincolo verrà valutato e non riuscirà, causando errori che potrebbero non essere correlati all'aggiornamento effettivo effettuato.
  3. La logica dell'applicazione che si basa sul vincolo per garantire che i dati siano validi potrebbe non riuscire.
  4. Query Optimizer non utilizzerà alcun vincolo abilitato in questo modo.

La vista di sistema sys.foreign_keys fornisce una certa visibilità sul problema. Nota che ha sia una is_disabledche una is_not_trustedcolonna. is_disabledindica se le future operazioni di manipolazione dei dati verranno convalidate rispetto al vincolo. is_not_trustedindica se tutti i dati attualmente nella tabella sono stati convalidati rispetto al vincolo.

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint;

I tuoi vincoli sono affidabili? Scoprire...

SELECT * FROM sys.foreign_keys WHERE is_not_trusted = 1;

9

Primo post :)

Per l'OP, la soluzione di kristof funzionerà, a meno che non ci siano problemi con dati enormi e problemi relativi al log delle transazioni in caso di grandi cancellazioni. Inoltre, anche con l'archiviazione tlog di riserva, poiché le cancellazioni scrivono nel tlog, l'operazione può richiedere MOLTO tempo per le tabelle con centinaia di milioni di righe.

Uso una serie di cursori per troncare e ricaricare frequentemente grandi copie di uno dei nostri enormi database di produzione. La soluzione ingegnerizzata tiene conto di più schemi, più colonne di chiavi esterne e, soprattutto, può essere elaborata per essere utilizzata in SSIS.

Implica la creazione di tre tabelle di gestione temporanea (tabelle reali) per ospitare gli script DROP, CREATE e CHECK FK, la creazione e l'inserimento di tali script nelle tabelle, quindi il ciclo sopra le tabelle e l'esecuzione. Lo script allegato è suddiviso in quattro parti: 1.) creazione e memorizzazione degli script nelle tre tabelle di staging (reali), 2.) esecuzione degli script FK di rilascio tramite un cursore uno per uno, 3.) Utilizzo di sp_MSforeachtable per troncare tutti i tabelle nel database diverse dalle nostre tre tabelle di gestione temporanea e 4.) esecuzione dell'FK di creazione e controllo degli script FK alla fine del pacchetto SSIS ETL.

Eseguire la parte di creazione dello script in un'attività Esegui SQL in SSIS. Esegui la parte "esegui Drop Script FK" in una seconda attività Esegui SQL. Inserisci lo script di troncamento in una terza attività Esegui SQL, quindi esegui qualsiasi altro processo ETL che devi fare prima di collegare gli script CREATE e CHECK in un'attività Esegui SQL finale (o due se lo desideri) alla fine del flusso di controllo.

L'archiviazione degli script in tabelle reali si è dimostrata preziosa quando la re-applicazione delle chiavi esterne ha esito negativo in quanto è possibile selezionare * da sync_CreateFK, copiare / incollare nella finestra della query, eseguirli uno alla volta e risolvere i problemi relativi ai dati una volta trova quelli che hanno fallito / non riescono ancora a riapplicare.

Non rieseguire nuovamente lo script se fallisce senza esserti assicurato di riapplicare tutte le chiavi esterne / i controlli prima di farlo, o molto probabilmente perderai un po 'di creazione e controllerai gli script fk quando le nostre tabelle di staging vengono eliminate e ricreato prima della creazione degli script da eseguire.

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

7

Trova il vincolo

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

Eseguire l'SQL generato da questo 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.

Nota: aggiunta soluzione per eliminare il vincolo in modo che la tabella possa essere eliminata o modificata senza errori di vincolo.


3

Fai clic con il pulsante destro del mouse sulla struttura della tabella e vai su Relazioni e scegli la chiave esterna nel riquadro sinistro e nel riquadro destro, imposta Imponi vincolo chiave esterna su "Sì" (per abilitare i vincoli di chiave esterna) o "No" (per disabilitarlo). inserisci qui la descrizione dell'immagine


3

La risposta contrassegnata con '905' sembra buona ma non funziona.

Il seguito ha funzionato per me. Eventuali vincoli Chiave primaria, Chiave univoca o Predefinito NON POSSONO essere disabilitati. Infatti, se 'sp_helpconstraint' 'mostra' n / a 'in status_enabled - Significa che NON può essere abilitato / disabilitato.

- Per generare script da DISATTIVARE

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

- Per generare script per ABILITARE

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

3

Dovresti effettivamente essere in grado di disabilitare i vincoli di chiave esterna allo stesso modo in cui disabiliti temporaneamente altri vincoli:

Alter table MyTable nocheck constraint FK_ForeignKeyConstraintName

Assicurati solo di disabilitare il vincolo sulla prima tabella elencata nel nome del vincolo. Ad esempio, se il mio vincolo di chiave esterna era FK_LocationsEmployeesLocationIdEmployeeId, vorrei utilizzare quanto segue:

Alter table Locations nocheck constraint FK_LocationsEmployeesLocationIdEmployeeId

anche se la violazione di questo vincolo produrrà un errore che non indica necessariamente quella tabella come fonte del conflitto.


1

Uno script per domarli tutti: questo combina i comandi di troncamento ed eliminazione con sp_MSforeachtable in modo da poter evitare di eliminare e ricreare i vincoli - basta specificare le tabelle che devono essere eliminate anziché troncate e per i miei scopi ho incluso un filtro schema aggiuntivo misura (testata nel 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

1

È possibile disabilitare temporaneamente i vincoli sulle tabelle, eseguire operazioni e ricostruirle.

Ecco un modo semplice per farlo ...

Disabilita tutti gli indici, comprese le chiavi primarie, che disabiliteranno tutte le chiavi esterne, quindi riattiva solo le chiavi primarie in modo da poter lavorare con esse ...

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

[Fai qualcosa, come caricare i dati]

Quindi riattivare e ricostruire gli indici ...

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

Sembrava promettente, ma @sqlviene sempre troncato. :(
Will Strohl,

1

Ho una versione più utile se sei interessato. Ho sollevato un po 'di codice da qui un sito Web in cui il collegamento non è più attivo. L'ho modificato per consentire una matrice di tabelle nella procedura memorizzata e popola le istruzioni drop, troncate, add prima di eseguirle tutte. Questo ti dà il controllo per decidere quali tabelle devono essere troncate.

/****** 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

Link morto nella tua risposta. Indica un articolo vuoto.
Neolisk,

Salve, potrebbe esserci un deadlink ma l'intero codice è specificato nel pezzo. Cosa c'è di sbagliato in questo?
Zak Willis,

Niente di male, ma probabilmente dovresti modificare la tua risposta e rimuovere il link non funzionante.
Neolisk,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.