66

I am running an SSIS package which will replace data for a few tables from FlatFiles to existing tables in a database.

My package will truncate the tables and then insert the new data. When I run my SSIS package, I get an exception because of the foreign keys.

Can I disable the constraints, run my import, then re-enable them?

Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
HaBo
  • 13,999
  • 36
  • 114
  • 206
  • 11
    Dear people from The Future: You can disable and re-enable all constraints in the database at once - see http://stackoverflow.com/a/161410 – brichins Feb 06 '13 at 00:41
  • 1
    @brichins Sometimes this undocumented stored procedure is not there in the database. This gives 0 records: `SELECT * FROM sys.all_objects WHERE name like 'sp_MSforeach%';`, so then an attempt at using it e.g. `EXEC sp_MSforeachtable SELECT 1 AS FOO` gives error message `Msg 2812, Level 16, State 62, Line 15 Could not find stored procedure 'sp_MSforeachtable'.` – TPPZ Apr 06 '17 at 14:34

9 Answers9

113

To disable foreign key constraints:

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 + N' NOCHECK CONSTRAINT ALL;
' FROM x;

EXEC sys.sp_executesql @sql;

To re-enable:

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 + N' WITH CHECK CHECK CONSTRAINT ALL;
' FROM x;

EXEC sys.sp_executesql @sql;

However, you will not be able to truncate the tables, you will have to delete from them in the right order. If you need to truncate them, you need to drop the constraints entirely, and re-create them. This is simple to do if your foreign key constraints are all simple, single-column constraints, but definitely more complex if there are multiple columns involved.

Here is something you can try. In order to make this a part of your SSIS package you'll need a place to store the FK definitions while the SSIS package runs (you won't be able to do this all in one script). So in some utility database, create a table:

CREATE TABLE dbo.PostCommand(cmd nvarchar(max));

Then in your database, you can have a stored procedure that does this:

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(N'./text()[1]', 'nvarchar(max)'), 1, 1, N'')
+ ') 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(N'./text()[1]', N'nvarchar(max)'), 1, 1, N'') + ');
' 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 sys.sp_executesql @sql;
END

Now when your SSIS package is finished, it should call a different stored procedure, which does:

DECLARE @sql nvarchar(max);

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

EXEC sys.sp_executesql @sql;

If you're doing all of this just for the sake of being able to truncate instead of delete, I suggest just taking the hit and running a delete. Maybe use bulk-logged recovery model to minimize the impact of the log. In general I don't see how this solution will be all that much faster than just using a delete in the right order.

In 2014 I published a more elaborate post about this here:

Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
  • Please see if you have any working code, because I have to Truncate the table, if not i think my Identity inserts may cause a problem. – HaBo Jul 24 '12 at 22:11
  • 1
    BY the way. I was trying to run your scripts for disable and enable. Disable runs with no issue, but when i try to enable i get conflicts of "Msg 547, Level 16, State 0, Line 1 The ALTER TABLE statement conflicted with the FOREIGN KEY constraint ..........." – HaBo Jul 24 '12 at 22:18
  • @HaBo well in the meantime did you insert data that violates the constraint? You can't enable the constraint if the table contains data that doesn't meet it. – Aaron Bertrand Jul 24 '12 at 22:29
  • @HaBo ...or maybe you have circular references. Those commands worked for me in a simple database. Hard for me to know what went wrong in yours. – Aaron Bertrand Jul 24 '12 at 22:37
  • Well I am worried now. Disable ran with no issue. so I don't have active constraints now. How can i check if All my constraints are active? – HaBo Jul 24 '12 at 23:20
  • 7
    @HaBo Try: `SELECT name, is_disabled FROM sys.foreign_keys;` P.S. this is why you run code samples you don't understand, given to you from strangers on a web site, on a test system first. – Aaron Bertrand Jul 24 '12 at 23:22
  • @HaBo Please don't edit error messages you received into the answer. And I can't help solve `FK_XXX already exists` on your system. Perhaps the drop constraints didn't work? The solution is straightforward but if you're only pulling parts of it, changing it etc., I can't help you. – Aaron Bertrand Jul 25 '12 at 16:04
  • I got what I needed with your help. So I am marking your answer. I used your code to store keys and drop. To restore: I had all my keys scripted so I am using that script to recreate the Keys. Thank you so much for your help. – HaBo Jul 25 '12 at 16:07
  • FYI: Drop worked perfectly. I tested SELECT * FROM sys.foreign_keys after i ran drop procedure. No keys were returned. This confirms Drop is good. Any ways thank you. I could complete my task. – HaBo Jul 25 '12 at 16:10
58

Use the built-in sp_msforeachtable stored procedure.

To disable all constraints:

EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT ALL";

To enable all constraints:

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

To drop all the tables:

EXEC sp_msforeachtable "DROP TABLE ?";
Ed Randall
  • 6,887
  • 2
  • 50
  • 45
  • 2 downvotes on this answer this week; wondering what the problem is? – Ed Randall Feb 23 '17 at 17:35
  • 12
    Maybe someone tried to execute these 3 queries, and especially the last one, on a production database :-) – carlo.borreo Mar 22 '17 at 12:52
  • 2
    Sadly, `sp_msforeachtable` doesn't exist in Azure SQL – Matt Jacobi Oct 03 '18 at 16:15
  • 1
    DISABLING and DROPPING constraints are two entirely different things. If you disable them, you won't be able to truncate the table, that's how SQL Server works. You have to DROP them, then truncate the table and then add constraints again. – HamsterWithPitchfork Aug 28 '19 at 09:02
  • More about sp_msforeachtable including the code so you can install it into your Azure SQL: https://www.sqlshack.com/an-introduction-to-sp_msforeachtable-run-commands-iteratively-through-all-tables-in-a-database/ – Ed Randall Jan 27 '22 at 14:55
8

There is an easy way to this.

-- Disable all the constraint in database
EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT all'

-- Enable all the constraint in database
EXEC sp_msforeachtable 'ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all'

Reference SQL SERVER – Disable All the Foreign Key Constraint in Database – Enable All the Foreign Key Constraint in Database

5

A good reference is given at : http://msdn.microsoft.com/en-us/magazine/cc163442.aspx under the section "Disabling All Foreign Keys"

Inspired from it, an approach can be made by creating a temporary table and inserting the constraints in that table, and then dropping the constraints and then reapplying them from that temporary table. Enough said here is what i am talking about

 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
NG.
  • 5,695
  • 2
  • 19
  • 30
  • to disable all constraints, one can try..... EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all" – NG. Jul 25 '12 at 03:54
  • and to enable them back exec sp_msforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all" – NG. Jul 25 '12 at 04:04
  • but these will disable all the constraints, so not sure whether it will be helpful in your case of ssis, it might help when we are truncating the table data – NG. Jul 25 '12 at 04:05
  • I'm not sure how a table variable helps... if this is being executed by an SSIS package, the last step is not going to have access to the table variable anymore. This is why my solution used a permanent table... those constraint definitions are still available even if the package fails, gets cancelled, the server crashes, etc. Also your script has a very simplistic view of foreign key constraints - it won't handle a multi-column foreign key (e.g. `FOREIGN KEY (a,b) REFERENCES dbo.foo(c,d)`). – Aaron Bertrand Jul 25 '12 at 11:36
  • Aaron Bertand : I appreciate your answer. – NG. Jul 25 '12 at 14:17
1

Disable all table constraints

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName

-- Enable all table constraints

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
1

In case you use a different database schemas than ".dbo" or your db is containing Pk´s, which are composed by several fields, please don´t use the the solution of Carter Medlin, otherwise you will damage your db!!!

When you are working with different schemas try this (don´t forget to make a backup of your database before!):

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

After doing some Fk-free actions, you can switch back with

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";
olaf870
  • 31
  • 3
  • Certainly fried everything. normally don't try these things, but had a backup. Unfortunately, it's not possible to truncate a table w/o completely removing the constraint, then adding it back. Disabling constraints will not work on MSSQL for truncating (using SQL2016 & earlier -not sure about the latest version) – MC9000 Dec 27 '18 at 07:22
0

not need to run queries to sidable FKs on sql. If you have a FK from table A to B, you should:

  • delete data from table A
  • delete data from table B
  • insert data on B
  • insert data on A

You can also tell the destination not to check constraints

enter image description here

Diego
  • 34,802
  • 21
  • 91
  • 134
  • 2
    You can't truncate a table that is being referenced by a foreign key constraint, even if the table is empty, and even if the constraint is disabled. Have you tried it? – Aaron Bertrand Jul 25 '12 at 11:34
  • your are right, I forgot about that detail, but you can run delete * from....and reset the identity – Diego Jul 25 '12 at 11:51
  • So please update your answer with that information. The user can't use truncate unless they drop the foreign keys. – Aaron Bertrand Jul 25 '12 at 11:54
0

Truncating the table wont be possible even if you disable the foreign keys.so you can use delete command to remove all the records from the table,but be aware if you are using delete command for a table which consists of millions of records then your package will be slow and your transaction log size will increase and it may fill up your valuable disk space.

If you drop the constraints it may happen that you will fill up your table with unclean data and when you try to recreate the constraints it may not allow you to as it will give errors. so make sure that if you drop the constraints,you are loading data which are correctly related to each other and satisfy the constraint relations which you are going to recreate.

so please carefully think the pros and cons of each method and use it according to your requirements

vimal vasudevan
  • 179
  • 1
  • 4
  • 17
-2

Disable all indexes (including the pk, which will disable all fks), then reenable the pks.

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

[Do your data load]

Then bring everything back to life...

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
  • 11,857
  • 5
  • 62
  • 68