FlatFilesからデータベース内の既存のテーブルにいくつかのテーブルのデータを置き換えるSSISパッケージを実行しています。
私のパッケージはテーブルを切り捨ててから、新しいデータを挿入します。SSISパッケージを実行すると、外部キーが原因で例外が発生します。
制約を無効にして、インポートを実行してから、再度有効にすることはできますか?
FlatFilesからデータベース内の既存のテーブルにいくつかのテーブルのデータを置き換えるSSISパッケージを実行しています。
私のパッケージはテーブルを切り捨ててから、新しいデータを挿入します。SSISパッケージを実行すると、外部キーが原因で例外が発生します。
制約を無効にして、インポートを実行してから、再度有効にすることはできますか?
外部キー制約を無効にするには:
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;
再度有効にするには:
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;
ただし、テーブルを切り捨てることはできません。正しい順序でテーブルから削除する必要があります。それらを切り捨てる必要がある場合は、制約を完全に削除して、それらを再作成する必要があります。これは、外部キー制約がすべて単純な単一列の制約である場合は簡単ですが、複数の列が含まれている場合は間違いなくより複雑になります。
これがあなたが試すことができるものです。これを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(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
これで、SSISパッケージが終了すると、次のような別のストアドプロシージャを呼び出す必要があります。
DECLARE @sql nvarchar(max);
SELECT @sql = cmd FROM other_database.dbo.PostCommand;
EXEC sys.sp_executesql @sql;
削除ではなく切り捨てを行うためだけにこれらすべてを実行している場合は、ヒットを取得して削除を実行することをお勧めします。たぶん、ログの影響を最小限に抑えるために、一括ログ回復モデルを使用します。一般に、このソリューションが、正しい順序で削除を使用するよりもはるかに高速になるかどうかはわかりません。
2014年に、これに関するより詳細な投稿をここに公開しました。
組み込みの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 の「DisableingAllForeignKeys」セクションに適切なリファレンスがあります。
そこから着想を得て、一時テーブルを作成し、そのテーブルに制約を挿入し、制約を削除して、その一時テーブルからそれらを再適用することで、アプローチを行うことができます。ここで私が話していることは十分に言われています
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
これには簡単な方法があります。
-- 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'
リファレンスSQLSERVER–データベースのすべての外部キー制約を無効にする–データベースのすべての外部キー制約を有効にする
すべてのテーブル制約を無効にする
ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName
-すべてのテーブル制約を有効にします
ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
「.dbo」とは異なるデータベーススキーマを使用している場合、またはデータベースに複数のフィールドで構成されるPkが含まれている場合は、Carter Medlinのソリューションを使用しないでください。使用すると、データベースが損傷します。
さまざまなスキーマで作業している場合は、これを試してください(前にデータベースのバックアップを作成することを忘れないでください!):
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";
SQLでsidableFKに対してクエリを実行する必要はありません。テーブルAからBへのFKがある場合は、次のことを行う必要があります。
制約をチェックしないように宛先に指示することもできます
外部キーを無効にしてもテーブルを切り捨てることはできません。したがって、deleteコマンドを使用してテーブルからすべてのレコードを削除できますが、数百万のレコードで構成されるテーブルに対してdeleteコマンドを使用している場合は、パッケージに注意してください。速度が低下し、トランザクションログのサイズが大きくなり、貴重なディスク領域がいっぱいになる可能性があります。
制約を削除すると、テーブルがクリーンでないデータでいっぱいになる可能性があり、制約を再作成しようとすると、エラーが発生するため、許可されない場合があります。したがって、制約を削除する場合は、相互に正しく関連し、再作成する制約関係を満たすデータをロードしていることを確認してください。
そのため、各方法の長所と短所を慎重に検討し、要件に応じて使用してください
すべてのインデックス(すべてのfksを無効にするpkを含む)を無効にしてから、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
[データの読み込みを行います]
その後、すべてを生き返らせます...
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