web-dev-qa-db-ja.com

すべての外部キー制約を一時的に無効にします

FlatFilesのいくつかのテーブルのデータをデータベース内の既存のテーブルに置き換えるSSISパッケージを実行しています。

私のパッケージはテーブルを切り捨ててから、新しいデータを挿入します。 SSISパッケージを実行すると、外部キーが原因で例外が発生します。

制約を無効にしてインポートを実行してから、再度有効にすることはできますか?

41
HaBo

外部キー制約を無効にするには:

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;

ただし、テーブルをtruncateすることはできません。正しい順序で削除する必要があります。それらをtruncateする必要がある場合は、制約を完全に削除して再作成する必要があります。これは、外部キー制約がすべて単純な単一列制約である場合は簡単ですが、複数の列が関係している場合は間違いなく複雑です。

試してみてください。これをSSISパッケージの一部にするには、SSISパッケージの実行中にFK定義を保存する場所が必要です(これを1つのスクリプトですべて実行することはできません)。そのため、いくつかのユーティリティデータベースで、テーブルを作成します。

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年に、私はこれについてより詳細な投稿をここに公開しました。

79
Aaron Bertrand

組み込みの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 ?";
31
Ed Randall

適切なリファレンスは次の場所にあります: 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
NG.

すべてのテーブル制約を無効にします

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName

-すべてのテーブル制約を有効にする

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
1
Alam Usmani

外部キーを無効にしてもテーブルを切り捨てることはできないため、deleteコマンドを使用してテーブルからすべてのレコードを削除できますが、何百万ものレコードで構成されるテーブルに対してdeleteコマンドを使用している場合はパッケージに注意してください遅くなり、トランザクションログのサイズが大きくなり、貴重なディスク容量がいっぱいになる可能性があります。

制約を削除すると、テーブルが汚れたデータでいっぱいになり、制約を再作成しようとするとエラーが発生するため、許可されない場合があります。そのため、制約を削除する場合は、相互に正しく関連し、再作成する制約関係を満たすデータをロードするようにしてください。

各方法の長所と短所を慎重に検討し、要件に応じて使用してください

0
vimal vasudevan

sQLでsidable FKへのクエリを実行する必要はありません。テーブルAからBへのFKがある場合、以下を行う必要があります。

  • テーブルAからデータを削除する
  • テーブルBからデータを削除する
  • bにデータを挿入
  • aにデータを挿入

宛先に制約をチェックしないように指示することもできます

enter image description here

0
Diego

「.dbo」とは異なるデータベーススキーマを使用する場合、またはデータベースに複数のフィールドで構成されるPkが含まれる場合は、Carter Medlinのソリューションを使用しないでください。そうしないと、dbが破損します!!!

異なるスキーマで作業している場合は、これを試してください(データベースのバックアップを作成することを忘れないでください!):

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フリーアクションを実行した後、元に戻すことができます

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";
0
olaf870