T-SQLのMERGEステートメントを使用して多くのレコードを挿入しようとしていますが、ソーステーブルに重複するレコードがある場合、クエリはINSERTに失敗します。失敗の原因は次のとおりです。
ソーステーブル内の重複レコードを無視するか、発生する可能性のある例外をキャッチするためにINSERTステートメントを試行/キャッチするようにMERGEステートメントを変更する方法を探しています(つまり、他のすべてのINSERTステートメントは発生する可能性のあるいくつかの悪い卵)-または、おそらく、この問題を回避するためのより良い方法がありますか?
これが私が説明しようとしていることのクエリ例です。以下の例では、一時テーブルに100kレコードを追加してから、それらのレコードをターゲットテーブルに挿入しようとします-
[〜#〜] edit [〜#〜]私の元の投稿では、例のテーブルに2つのフィールドのみを含めて、SO friends to give DISTINCT MERGEステートメントでの重複を回避するための解決策。私の実際の問題では、テーブルに15個のフィールドがあり、そのうち2個のフィールドがCLUSTERED PRIMARY KEYであることに言及する必要があります。したがって、DISTINCTキーワードは機能しません。 15のフィールドすべてを選択し、2つのフィールドに基づく重複を無視する必要があります。
以下のクエリを更新して、もう1つのフィールドcol4を含めました。 MERGEにcol4を含める必要がありますが、col2とcol3のみが一意であることを確認するだけで済みます。
-- Create the source table
CREATE TABLE #tmp (
col2 datetime NOT NULL,
col3 int NOT NULL,
col4 int
)
GO
-- Add a bunch of test data to the source table
-- For testing purposes, allow duplicate records to be added to this table
DECLARE @loopCount int = 100000
DECLARE @loopCounter int = 0
DECLARE @randDateOffset int
DECLARE @col2 datetime
DECLARE @col3 int
DECLARE @col4 int
WHILE (@loopCounter) < @loopCount
BEGIN
SET @randDateOffset = Rand() * 100000
SET @col2 = DATEADD(MI,@randDateOffset,GETDATE())
SET @col3 = Rand() * 1000
SET @col4 = Rand() * 10
INSERT INTO #tmp
(col2,col3,col4)
VALUES
(@col2,@col3,@col4);
SET @loopCounter = @loopCounter + 1
END
-- Insert the source data into the target table
-- How do we make sure we don't attempt to INSERT a duplicate record? Or how can we
-- catch exceptions? Or?
MERGE INTO dbo.tbl1 AS tbl
USING (SELECT * FROM #tmp) AS src
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3)
WHEN NOT MATCHED THEN
INSERT (col2,col3,col4)
VALUES (src.col2,src.col3,src.col4);
GO
新しい仕様に解決しました。 col4の最大値のみを挿入する:今回は、重複する行を防ぐためにgroupbyを使用しました。
MERGE INTO dbo.tbl1 AS tbl
USING (SELECT col2,col3, max(col4) col4 FROM #tmp group by col2,col3) AS src
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3)
WHEN NOT MATCHED THEN
INSERT (col2,col3,col4)
VALUES (src.col2,src.col3,src.col4);
ソースに重複があり、MERGEを完全に使用していない場合は、INSERTを使用します。
INSERT dbo.tbl1 (col2,col3)
SELECT DISTINCT col2,col3
FROM #tmp src
WHERE NOT EXISTS (
SELECT *
FROM dbo.tbl1 tbl
WHERE tbl.col2 = src.col2 AND tbl.col3 = src.col3)
MERGEが失敗する理由は、行ごとにチェックされないためです。一致しないものがすべて見つかった場合、これらすべてを挿入しようとします。同じバッチ内ですでに一致している行はチェックされません。
これは、アトミック操作の初期のデータ変更が後のデータ変更に影響を与える "ハロウィーンの問題" を少し思い出させます:それは正しくありません
GROUP BYの代わりに、分析関数を使用して、マージする重複レコードのセットから特定のレコードを選択できます。
MERGE INTO dbo.tbl1 AS tbl
USING (
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY col2, col3 ORDER BY ModifiedDate DESC) AS Rn
FROM #tmp
) t
WHERE Rn = 1 --choose the most recently modified record
) AS src
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3)