Temporarily disable all foreign key constraints
我正在运行一个ssis包,它将替换从平面文件到数据库中现有表的几个表的数据。
我的包将截断表,然后插入新数据。当我运行我的ssis包时,由于外键,我得到了一个异常。
我可以禁用约束,运行导入,然后重新启用它们吗?
要禁用外键约束:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; |
重新启用:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; |
但是,您将无法截断这些表,必须按正确的顺序从中删除。如果需要截断它们,则需要完全删除约束,然后重新创建它们。如果您的外键约束都是简单的单列约束,那么这很简单,但是如果涉及多个列,则更复杂。
这是你可以尝试的东西。为了使其成为SSIS包的一部分,在运行SSIS包时需要一个存储FK定义的位置(您将无法在一个脚本中完成这一切)。因此,在一些实用程序数据库中,创建一个表:
1 | CREATE TABLE dbo.PostCommand(cmd NVARCHAR(MAX)); |
然后,在数据库中,可以有一个执行此操作的存储过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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 |
现在,当您的SSIS包完成时,它应该调用另一个存储过程,该过程执行以下操作:
1 2 3 4 5 | DECLARE @sql NVARCHAR(MAX); SELECT @sql = cmd FROM other_database.dbo.PostCommand; EXEC sp_executesql @sql; |
如果你这样做只是为了能够截短而不是删除,我建议你只是点击并运行一个删除。可以使用大容量日志恢复模型来最小化日志的影响。一般来说,我看不出这个解决方案比按正确的顺序使用删除快得多。
2014年,我在这里发表了一篇更详细的文章:
- 删除并重新创建SQL Server中的所有外键约束
使用内置的sp msforeachtable存储过程。
要禁用所有约束:
1 | EXEC sp_msforeachtable"ALTER TABLE ? NOCHECK CONSTRAINT ALL"; |
要启用所有约束:
1 | EXEC sp_msforeachtable"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL"; |
删除所有表:
1 | EXEC sp_msforeachtable"DROP TABLE ?"; |
请访问:http://msdn.microsoft.com/en-us/magazine/cc163442.aspx。在"禁用所有外键"部分下
从中得到启发,可以通过创建一个临时表并在该表中插入约束,然后删除这些约束,然后从该临时表中重新应用它们来制定方法。我说的够多了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 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 |
禁用所有表约束
1 | ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName |
--启用所有表约束
1 | ALTER TABLE TableName CHECK CONSTRAINT ConstraintName |
如果使用的数据库架构与".dbo"不同,或者数据库中包含由多个字段组成的pk,请不要使用carter medlin的解决方案,否则会损坏数据库!!!!
使用不同的模式时,请尝试此操作(不要忘记以前备份数据库!):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
在执行了一些自由FK操作后,您可以使用
1 2 3 4 5 6 7 8 9 10 | 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"; |
即使禁用外键,也无法截断表。delete命令删除表中的所有记录,但如果使用delete,请注意对于包含数百万条记录的表的命令,则包将变慢。您的事务日志大小将会增加,可能会填满您宝贵的磁盘空间。
如果您删除约束,可能会出现不干净的数据填满您的表的情况。当您试图重新创建约束时,它可能不允许您这样做,因为它会给出错误。因此,请确保如果删除约束,则正在加载彼此正确相关的数据,并满足要重新创建的约束关系。
因此,请仔细考虑每种方法的优缺点,并根据您的要求使用。
不需要在SQL上运行对Sidable FK的查询。如果您有从表A到表B的FK,您应该:
- 从表A中删除数据
- 从表B中删除数据
- 在B上插入数据
- 在上插入数据
您还可以告诉目的地不要检查约束
禁用所有索引(包括将禁用所有FK的pk),然后重新启用pk。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
[加载数据]
然后把所有的东西都复活…
1 2 3 4 5 6 7 8 9 | 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 |