私は現在、次のテーブルでtest_db
(target
)およびtablebackups
(source
)と呼ばれる別のデータベース上の同じテーブル.
IF OBJECT_ID('[dbo].[tblBCatalogueType]') IS NOT NULL
DROP TABLE [dbo].[tblBCatalogueType]
GO
CREATE TABLE [dbo].[tblBCatalogueType] (
[sintCatalogueTypeID] SMALLINT IDENTITY(1,1) NOT NULL,
[blnIsCurrent] BIT NOT NULL,
[tsRowVersion] TIMESTAMP NOT NULL,
CONSTRAINT [PK_tblProdCatalogueType]
PRIMARY KEY CLUSTERED ([sintCatalogueTypeID] asc)
WITH FILLFACTOR = 97)
私のtlbBCatalogueType
テーブルを参照しているテーブルを見ると、次のリストが表示されます。
外部キー制約を見つけるスクリプト:
----------------------------------------------------------------------------
declare @referenced_table sysname
declare @ref_Obj int
declare @table sysname
declare @Obj int
select @table = 'dbo.tblBCatalogueType' --'tblBCataloguePriceSetItem'
select @referenced_table = 'dbo.tblBCatalogueType' --'dbo.tblBCataloguePriceSet'
select @obj = OBJECT_ID(@table)
select @ref_obj = OBJECT_ID(@referenced_table)
SELECT obj.name AS FK_NAME,
sch.name AS [schema_name],
tab1.name AS [table],
col1.name AS [column],
sch.name AS [referenced_schema],
tab2.name AS [referenced_table],
col2.name AS [referenced_column]
FROM sys.foreign_key_columns fkc
INNER JOIN sys.objects obj
ON obj.object_id = fkc.constraint_object_id
INNER JOIN sys.tables tab1
ON tab1.object_id = fkc.parent_object_id
INNER JOIN sys.schemas sch
ON tab1.schema_id = sch.schema_id
INNER JOIN sys.columns col1
ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
INNER JOIN sys.tables tab2
ON tab2.object_id = fkc.referenced_object_id
INNER JOIN sys.schemas sch2
ON tab2.schema_id = sch2.schema_id
INNER JOIN sys.columns col2
ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
where 1=1
AND ( ( @OBJ IS NULL OR @obj = tab1.object_id)
OR ( @ref_obj IS NULL OR @ref_obj = tab2.object_id ))
これは私のマージステートメントです:
SET NOCOUNT ON;
DECLARE @TranCountAtStart INT;
SET @TranCountAtStart = @@TRANCOUNT;
-- SELECT @@TRANCOUNT,XACT_STATE();
IF @TranCountAtStart = 0
BEGIN TRANSACTION
ELSE
SAVE TRANSACTION USP_Procedure_Name;
BEGIN TRY
-------------------------------------------------------------------------------------------
-- the dbo.tblBCataloguePriceSet must be run before this merge.
-- Message: The MERGE statement conflicted with the REFERENCE constraint "fk_CataloguePriceSet_CatalogueType".
-- The conflict occurred in database "Bocss2", table "dbo.tblBCataloguePriceSet", column 'sintCatalogueTypeID'.
IF OBJECT_ID( 'tablebackups.dbo.tblBCatalogueType_log') IS NOT NULL DROP TABLE tablebackups.dbo.tblBCatalogueType_log;
CREATE TABLE tablebackups.dbo.tblBCatalogueType_log(
ChangeType NVARCHAR(10)
,sintCatalogueTypeID SMALLINT NOT NULL
,DateTimeChanged DateTime NOT NULL);
BEGIN TRANSACTION T1
SET IDENTITY_INSERT dbo.tblBCatalogueType ON;
SELECT @@TRANCOUNT
SELECT XACT_STATE()
MERGE dbo.tblBCatalogueType AS TARGET
USING tablebackups.dbo.tblBCatalogueType AS SOURCE
ON TARGET.sintCatalogueTypeID = SOURCE.sintCatalogueTypeID
WHEN MATCHED
THEN UPDATE SET
[blnIsCurrent] = SOURCE.blnIsCurrent
WHEN NOT MATCHED BY TARGET
THEN INSERT( [sintCatalogueTypeID]
,[blnIsCurrent]
)
VALUES(
SOURCE.[sintCatalogueTypeID]
,SOURCE.[blnIsCurrent]
)
WHEN NOT MATCHED BY SOURCE
THEN DELETE
--$action specifies a column of type nvarchar(10)
--in the OUTPUT clause that returns one of three
--values for each row: 'INSERT', 'UPDATE', or 'DELETE',
--according to the action that was performed on that row
-------------------------------------
OUTPUT
$ACTION ChangeType,
coalesce (inserted.sintCatalogueTypeID, deleted.sintCatalogueTypeID) sintCatalogueTypeID,
Getdate () DateTimeChanged
INTO tablebackups.dbo.tblBCatalogueType_log
-------------------------------------
;
SELECT @@ROWCOUNT;
SET IDENTITY_INSERT dbo.tblBCatalogueType OFF;
COMMIT TRANSACTION T1
-------------------------------------------------------------------------------------------
END TRY
BEGIN CATCH
SET IDENTITY_INSERT dbo.tblBCatalogueType OFF;
DECLARE @ERRORMESSAGE NVARCHAR(512),
@ERRORSEVERITY INT,
@ERRORNUMBER INT,
@ERRORSTATE INT,
@ERRORPROCEDURE SYSNAME,
@ERRORLINE INT,
@XASTATE INT
SELECT
@ERRORMESSAGE = ERROR_MESSAGE(),
@ERRORSEVERITY = ERROR_SEVERITY(),
@ERRORNUMBER = ERROR_NUMBER(),
@ERRORSTATE = ERROR_STATE(),
@ERRORPROCEDURE = ERROR_PROCEDURE(),
@ERRORLINE = ERROR_LINE()
SET @ERRORMESSAGE =
(
SELECT CHAR(13) +
'Message:' + SPACE(1) + @ErrorMessage + SPACE(2) + CHAR(13) +
'Error:' + SPACE(1) + CONVERT(NVARCHAR(50),@ErrorNumber) + SPACE(1) + CHAR(13) +
'Severity:' + SPACE(1) + CONVERT(NVARCHAR(50),@ErrorSeverity) + SPACE(1) + CHAR(13) +
'State:' + SPACE(1) + CONVERT(NVARCHAR(50),@ErrorState) + SPACE(1) + CHAR(13) +
'Routine_Name:' + SPACE(1) + coalesce(@ErrorProcedure,'') + SPACE(1) + CHAR(13) +
'Line:' + SPACE(1) + CONVERT(NVARCHAR(50),@ErrorLine) + SPACE(1) + CHAR(13) +
'Executed As:' + SPACE(1) + SYSTEM_USER + SPACE(1) + CHAR(13) +
'Database:' + SPACE(1) + DB_NAME() + SPACE(1) + CHAR(13) +
'OSTime:' + SPACE(1) + CONVERT(NVARCHAR(25),CURRENT_TIMESTAMP,121) + CHAR(13)
)
SELECT @XASTATE = XACT_STATE();
IF @XASTATE = - 1
ROLLBACK;
IF @XASTATE = 1
AND @TranCountAtStart = 0
ROLLBACK
IF @XASTATE = 1
AND @TranCountAtStart > 0
ROLLBACK TRANSACTION USP_Procedure_Name;
--We can also save the error details to a table for later reference here.
RAISERROR (@ERRORMESSAGE,16,1)
END CATCH
どうすればマージにエラー処理プロセスを追加できますか?
おそらくそれは削除で壊れます。
WHEN NOT MATCHED BY SOURCE
THEN DELETE
ここにそれを処理する何かがあるべきです。
単純にして、制約違反で壊れた場合は、レコードを削除しないでください。
どうすればこれを達成できますか?
さて、「レコードを削除しないでください」はさておき、外部キーを 削除カスケードまたは削除セットnullの場合 に変更できます。これにより、親を削除したり、子キーをnullに設定したりすると、すべてが削除されます。
それらは常に素晴らしい行動とは限りません。たとえば、顧客がアカウントを削除した場合、おそらくすべての注文履歴と税金請求書も削除したくないでしょう。
醜い、恐ろしいが一般的な代替手段は次のとおりです。
スキーマが変更されないことがわかっている場合(またはそれを実行する場合)、削除したくないキーを除外するサブクエリを含めることができます。これは悪くないです。
本当に醜くしたい場合は、そこでプロシージャを使用して動的SQLを作成し、同じことを行うことができます(それを一時テーブルに入れて、結果から除外します)。必要がない限り、これを行わないでください。
これらを[削除ではなく]トリガーと組み合わせて使用できます。これはおそらく最悪です。