暂时禁用所有外键约束
我正在运行一个SSIS包,它将从FlatFiles中的几个表中的数据replace为数据库中现有的表。
我的包将截断表,然后插入新的数据。 当我运行我的SSIS包,我得到一个exception,因为外键。
我可以禁用约束,运行我的导入,然后重新启用它们?
要禁用外键约束:
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;
要重新启用:
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;
但是,您将无法截断表,您将不得不以正确的顺序从它们中删除。 如果您需要截断它们,则需要完全删除约束条件,然后重新创build它们。 如果你的外键约束都是简单的单列约束,那么做起来很简单,但是如果涉及到多列的话,这肯定会更加复杂。
这是你可以尝试的东西。 为了使它成为你的SSIS包的一部分,在SSIS包运行的时候,你需要一个存储FK定义的地方(你不能在一个脚本中完成这一切)。 所以在一些实用程序数据库中,创build一个表:
CREATE TABLE dbo.PostCommand(cmd NVARCHAR(MAX));
然后在你的数据库中,你可以有一个这样的存储过程:
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包完成后,它应该调用一个不同的存储过程,它会:
DECLARE @sql NVARCHAR(MAX); SELECT @sql = cmd FROM other_database.dbo.PostCommand; EXEC sp_executesql @sql;
如果你只是为了能够截断而不是删除而做所有这些,我build议只需要点击并运行删除。 也许使用大容量日志恢复模式来减less日志的影响。 一般来说,我不明白这个解决scheme比以正确的顺序使用删除更快。
2014年,我在这里发表了更详细的文章:
- 在SQL Server中删除并重新创build所有外键约束
使用内置的sp_msforeachtable存储过程。
要禁用所有约束:
EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT ALL";
要启用所有约束:
EXEC sp_msforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL";
要删除所有表格:
EXEC sp_msforeachtable "DROP TABLE ?";
在“禁用所有外键”部分下的http://msdn.microsoft.com/en-us/magazine/cc163442.aspx中提供了一个很好的参考。;
受其启发,可以通过创build临时表并在该表中插入约束,然后删除约束,然后将其从临时表中重新应用来实现方法。 这里所说的就是我所说的
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
禁用所有表约束
ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName
– 启用所有表约束
ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
不需要在sql上对sidable FKs运行查询。 如果你从表A到表B有一个FK,你应该:
- 删除表A中的数据
- 删除表B中的数据
- 在B上插入数据
- 在A上插入数据
您也可以告诉目的地不要检查约束
即使禁用外键,也不能截断表。因此,您可以使用delete命令从表中删除所有logging,但要注意,如果对包含数百万条logging的表使用delete命令,那么您的包会很慢,您的事务日志大小将会增加,并且可能会填满您宝贵的磁盘空间。
如果放弃约束条件,可能会发生这样的情况,即您将使用不清晰的数据填充表格,而当您尝试重新创build约束条件时,可能不会允许您input错误。 所以请确保如果您放弃约束,您正在加载正确相关的数据,并满足您将要重新创build的约束关系。
所以请仔细考虑每种方法的优缺点,并根据您的要求使用它