暂时禁用所有外键约束

时间:2012-07-24 21:50:41

标签: sql-server sql-server-2008 ssis constraints

我正在运行一个SSIS包,它将把几个表的数据从FlatFiles替换为数据库中的现有表。

我的包将截断表,然后插入新数据。当我运行我的SSIS包时,由于外键我得到一个例外。

我可以禁用约束,运行导入,然后重新启用它们吗?

9 个答案:

答案 0 :(得分:78)

禁用外键约束:

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;

但是,您将无法截断表,您必须按正确的顺序从中删除它们。如果您需要截断它们,则需要完全删除约束,然后重新创建它们。如果外键约束都是简单的单列约束,这很容易做到,但如果涉及多个列,则肯定会更复杂。

这是你可以尝试的东西。为了使其成为SSIS包的一部分,您需要在SSIS包运行时存储FK定义的位置(您将无法在一个脚本中完成所有操作)。所以在一些实用程序数据库中,创建一个表:

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;

如果你只是为了能够截断而不是删除而完成所有这些操作,我建议只需点击并运行删除即可。也许使用批量日志恢复模型来最小化日志的影响。总的来说,我没有看到这个解决方案如何比以正确的顺序使用删除更快。

2014年,我在这里发表了一篇更详细的文章:

答案 1 :(得分:30)

使用内置的 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 ?";

答案 2 :(得分:3)

http://msdn.microsoft.com/en-us/magazine/cc163442.aspx给出了一个很好的参考 在“禁用所有外键”部分下

受其启发,可以通过创建临时表并在该表中插入约束,然后删除约束然后从该临时表重新应用它们来实现一种方法。这里说的就是我所说的

 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

答案 3 :(得分:1)

禁用所有表约束

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName

- 启用所有表约束

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName

答案 4 :(得分:0)

不需要在sql上对sidable FK运行查询。如果您从表A到B有FK,您应该:

  • 从表A中删除数据
  • 从表B中删除数据
  • 在B
  • 上插入数据
  • 在A
  • 上插入数据

您还可以告诉目的地不要检查约束

enter image description here

答案 5 :(得分:0)

即使禁用外键,也无法截断表格。所以你可以使用 delete命令从表中删除所有记录,但要注意是否使用了删除 对于包含数百万条记录的表的命令,那么您的包将会很慢 并且您的事务日志大小将增加,它可能会占用宝贵的磁盘空间。

如果你放弃约束,你可能会用不洁净的数据填满你的桌子 当你尝试重新创建约束时,它可能不允许你,因为它会给出错误。 因此,请确保如果删除约束,则加载彼此正确相关的数据并满足要重新创建的约束关系。

所以请仔细考虑每种方法的优缺点,并根据您的要求使用

答案 6 :(得分:0)

如果您使用的数据库模式与“ .dbo”不同,或者您的数据库包含由多个字段组成的Pk,请不要使用卡特·梅德林的解决方案,否则会损坏您的数据库!!!

在使用不同的架构时,请尝试以下操作(不要忘记之前对数据库进行备份!):

// definition
function loadScript(scriptUrl) {
  const script = document.createElement('script');
  script.src = scriptUrl;
  document.body.appendChild(script);

  return new Promise(res => {
    script.onLoad = function() {
      res();
    }
  });
}

// use
loadScript('http:// .... /jquery.js')
  .then(() => {
    console.log('Script loaded!');
  });

执行一些免Fk动作后,您可以使用

db, err := gorm.Open("postgres", "host='postgres'&user:docker&port=5432&dbname='docker'&password='password'&sslmode=disable")

答案 7 :(得分:0)

有一个简单的方法。

-- 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'

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

答案 8 :(得分:-2)

禁用所有索引(包括pk,它将禁用所有fks),然后重新启用pk。

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

[是否加载数据]

然后将一切恢复原状...

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