Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Disattiva temporaneamente tutti i vincoli di chiave esterna

Per disabilitare i vincoli di chiave esterna:

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;

Per riattivare:

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;

Tuttavia, non potrai troncare le tabelle, dovrai eliminarle nell'ordine corretto. Se devi troncare loro, è necessario eliminare completamente i vincoli e ricrearli. Questo è semplice da fare se i tuoi vincoli di chiave esterna sono tutti semplici vincoli a colonna singola, ma decisamente più complessi se sono coinvolte più colonne.

Ecco qualcosa che puoi provare. Per renderlo parte del tuo pacchetto SSIS, avrai bisogno di un posto dove archiviare le definizioni FK mentre il pacchetto SSIS è in esecuzione (non sarai in grado di farlo tutto in un unico script). Quindi in alcuni database di utilità, crea una tabella:

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

Quindi nel tuo database, puoi avere una procedura memorizzata che fa questo:

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

Ora, quando il tuo pacchetto SSIS è terminato, dovrebbe chiamare una procedura memorizzata diversa, che fa:

DECLARE @sql NVARCHAR(MAX);

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

EXEC sp_executesql @sql;

Se stai facendo tutto questo solo per poter troncare invece di eliminare, ti suggerisco di prendere il colpo ed eseguire un'eliminazione. È possibile utilizzare il modello di ripristino con registrazione di massa per ridurre al minimo l'impatto del registro. In generale, non vedo come questa soluzione sarà molto più veloce del semplice utilizzo di un'eliminazione nell'ordine giusto.

Nel 2014 ho pubblicato un post più elaborato su questo qui:

  • Elimina e ricrea tutti i vincoli di chiave esterna in SQL Server