Temporarily disable all foreign key constraints
Asked Answered
P

9

68

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?

Pettitoes answered 24/7, 2012 at 21:50 Comment(2)
Dear people from The Future: You can disable and re-enable all constraints in the database at once - see stackoverflow.com/a/161410Aguascalientes
@Aguascalientes 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'.Katy
G
118

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:

Grog answered 24/7, 2012 at 22:1 Comment(11)
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.Pettitoes
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 ..........."Pettitoes
@Pettitoes 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.Grog
@Pettitoes ...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.Grog
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?Pettitoes
@Pettitoes 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.Grog
@Pettitoes 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.Grog
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.Pettitoes
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.Pettitoes
I am fairly sure this method will also disable any WITH CHECK primary keys. If you wish to only disable foreign keys, you can adjust the script to use the "name" of the constraint and ALTER TABLE ... NOCHECK CONSTRAINT ' + name + N';'Parenthesize
@Parenthesize What's a "with check primary key"? How do you disable a primary key? If you change the last line of the first query, you also need to change the CTE to not use DISTINCT and to also pull each FK name. Maybe you meant to only disable specific foreign keys to avoid disabling any check constraints on any tables that happen to also have outbound foreign keys?Grog
C
59

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 ?";
Chacon answered 17/12, 2014 at 10:57 Comment(5)
2 downvotes on this answer this week; wondering what the problem is?Chacon
Maybe someone tried to execute these 3 queries, and especially the last one, on a production database :-)Headwind
Sadly, sp_msforeachtable doesn't exist in Azure SQLDysentery
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.Colincolinson
More about sp_msforeachtable including the code so you can install it into your Azure SQL: sqlshack.com/…Chacon
K
9

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

Kiley answered 3/8, 2020 at 9:27 Comment(0)
S
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
Stomachic answered 25/7, 2012 at 3:48 Comment(5)
to disable all constraints, one can try..... EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"Stomachic
and to enable them back exec sp_msforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"Stomachic
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 dataStomachic
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)).Grog
Aaron Bertand : I appreciate your answer.Stomachic
O
1

Disable all table constraints

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName

-- Enable all table constraints

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
Offertory answered 17/12, 2014 at 9:57 Comment(1)
Disabling isn't enough to allow truncate.Grog
S
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";
Stannfield answered 12/12, 2018 at 13:39 Comment(1)
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)Borborygmus
S
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

Shoddy answered 25/7, 2012 at 8:41 Comment(3)
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?Grog
your are right, I forgot about that detail, but you can run delete * from....and reset the identityShoddy
So please update your answer with that information. The user can't use truncate unless they drop the foreign keys.Grog
C
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

Coetaneous answered 29/10, 2013 at 6:39 Comment(0)
E
-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
Etty answered 1/8, 2018 at 19:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.