次のようなものがあるとします。
ソーステーブル(変数):
Values (
LeftId INT NOT NULL,
RightId INT NOT NULL,
CustomValue varchar(100) NULL
)
ターゲットテーブル:
Mapping (
LeftId INT NOT NULL,
RightId INT NOT NULL,
CustomValue varchar(100) NULL
)
以下のルールでValues
をTarget
にマージしたい:
source.LeftId = target.LeftId AND source.RightId = target.RightId
に一致CustomValue
を更新しますLeftId
と一致するターゲット内の一致しない値を削除します。つまり、LefId
s私がマージしているもの。(その最後のルールは説明するのが難しい、ごめんなさい!)
ソース:
1, 10, foo
1, 11, foo
ターゲット:
1, 10, bar
1, 12, foo
2, 20, car
結果ターゲット:
1, 10, foo (updated)
1, 11, foo (inserted)
1, 12, foo (deleted)
2, 20, car (unchanged)
これが私がこれまで持ってきたもので、update
とinsert
を処理します:
MERGE Mapping AS target
USING (SELECT LeftId, RightId, CustomValue FROM @Values)
AS source (LeftId, RightId, CustomValue)
ON target.LeftId = source.LeftId
AND target.RightId = source.RightId
WHEN NOT MATCHED THEN
INSERT (LeftId, RightId, CustomValue)
VALUES (source.LeftId, source.RightId, source.CustomValue)
WHEN MATCHED THEN
UPDATE SET
CustomValue = source.CustomValue;
ルールのdelete
部分をどのように実行しますか?
これは、私が考えていた個別のDELETE
操作です。
DELETE m
FROM dbo.Mapping AS m
WHERE EXISTS
(SELECT 1 FROM @Values WHERE LeftID = m.LeftID)
AND NOT EXISTS
(SELECT 1 FROM @Values WHERE LeftID = m.LeftID AND RightID = m.RightID);
私が ここでの概要、左の反セミ結合の場合)は、NOT EXISTS
パターンがLEFT JOIN / NULL
パターンよりも多くの場合 よりも優れています。
全体的な目標が明快さかパフォーマンスかはわからないため、NOT MATCHED BY source
オプションよりも要件に適しているかどうかを判断できるのはあなただけです。確実に知るためには、計画を定性的に、計画やランタイムメトリックを定量的に調べる必要があります。
MERGE
コマンドが複数の独立したステートメントで発生する競合状態から保護することを期待している場合は、次のように変更して、trueであることを確認してください。
MERGE dbo.Mapping WITH (HOLDLOCK) AS target
( Dan Guzmanのブログ投稿 から。)
個人的には、MERGE
を使わずにこれをすべて実行します他の理由の中で、未解決のバグがあるため 。そして Paul Whiteは別のDMLステートメントも推奨するようです 。
そして、これが私がスキーマ接頭辞を追加した理由です: 作成、影響を与えるときなど、常にスキーマによってオブジェクトを参照する必要があります 。
CTEのターゲットテーブルから考慮する必要のある行を除外し、CTEをマージのターゲットとして使用できます。
WITH T AS
(
SELECT M.LeftId,
M.RightId,
M.CustomValue
FROM @Mappings AS M
WHERE EXISTS (SELECT *
FROM @Values AS V
WHERE M.LeftId = V.LeftId)
)
MERGE T
USING @Values AS S
ON T.LeftId = S.LeftId and
T.RightId = S.RightId
WHEN NOT MATCHED BY TARGET THEN
INSERT (LeftId, RightId, CustomValue)
VALUES (S.LeftId, S.RightId, S.CustomValue)
WHEN MATCHED THEN
UPDATE SET CustomValue = S.CustomValue
WHEN NOT MATCHED BY SOURCE THEN
DELETE
;
_WHEN NOT MATCHED BY SOURCE
_句を使用して、次のように条件を追加できます。
MS SQL Server 2008スキーマセットアップ:
_CREATE TABLE dbo.Vals (
LeftId INT NOT NULL,
RightId INT NOT NULL,
CustomValue varchar(100) NULL
);
CREATE TABLE dbo.Mapping (
LeftId INT NOT NULL,
RightId INT NOT NULL,
CustomValue varchar(100) NULL
);
INSERT INTO dbo.Vals(LeftId,RightId,CustomValue)
VALUES(1, 10, 'foo10'),(1, 11, 'foo11');
INSERT INTO dbo.Mapping(LeftId,RightId,CustomValue)
VALUES(1, 10, 'bar'),(1, 12, 'foo'),(2, 20, 'car');
_
クエリ1:
_MERGE dbo.Mapping WITH(HOLDLOCK) AS target
USING (SELECT LeftId, RightId, CustomValue FROM dbo.Vals)
AS source (LeftId, RightId, CustomValue)
ON target.LeftId = source.LeftId
AND target.RightId = source.RightId
WHEN NOT MATCHED THEN
INSERT (LeftId, RightId, CustomValue)
VALUES (source.LeftId, source.RightId, source.CustomValue)
WHEN MATCHED THEN
UPDATE SET
CustomValue = source.CustomValue
WHEN NOT MATCHED BY SOURCE AND EXISTS(SELECT 1 FROM dbo.Vals iVals WHERE target.LeftId = iVals.LeftId) THEN
DELETE
OUTPUT $action AS Action,
INSERTED.LeftId AS INS_LeftId,INSERTED.RightId AS INS_RightId,INSERTED.CustomValue AS INS_Val,
DELETED.LeftId AS DEL_LeftId,DELETED.RightId AS DEL_RightId,DELETED.CustomValue AS DEL_Val;
_
結果:
_| ACTION | INS_LEFTID | INS_RIGHTID | INS_VAL | DEL_LEFTID | DEL_RIGHTID | DEL_VAL |
------------------------------------------------------------------------------------
| INSERT | 1 | 11 | foo11 | (null) | (null) | (null) |
| UPDATE | 1 | 10 | foo10 | 1 | 10 | bar |
| DELETE | (null) | (null) | (null) | 1 | 12 | foo |
_
クエリ2:
_SELECT * FROM dbo.Mapping;
_
結果:
_| LEFTID | RIGHTID | CUSTOMVALUE |
----------------------------------
| 1 | 10 | foo10 |
| 2 | 20 | car |
| 1 | 11 | foo11 |
_
各行に対して実行されたアクションを示すために、MERGE
ステートメントに出力句を追加しました。
他のユーザーがコメントしているように、競合状態を防ぐために、ターゲットテーブルにWITH(HOLDLOCK)
ヒントも提供する必要があります。
これが私が思いついたものです。フィードバックは大歓迎です!
DECLARE @Values TABLE(LeftId INT, RightId INT, CustomValue VARCHAR(100))
DECLARE @Mappings TABLE(LeftId INT, RightId INT, CustomValue VARCHAR(100))
-- the incoming values
INSERT INTO @Values VALUES (1, 10, 'bar2'), (1, 11, 'foo')
-- the existing table
INSERT INTO @Mappings VALUES (1, 10, 'bar'), (1, 12, 'foo'), (2, 20, 'car')
delete
パーツを個別に処理します。DELETE M
FROM @Mappings M
JOIN (SELECT DISTINCT LeftId FROM @Values) DistinctLeftIds ON M.LeftId = DistinctLeftIds.LeftId
LEFT JOIN @Values V ON M.LeftId = V.LeftId AND M.RightId = V.RightId
WHERE V.LeftId IS NULL
MERGE @Mappings AS target
USING (SELECT LeftId, RightId, CustomValue FROM @Values)
AS source (LeftId, RightId, CustomValue)
ON target.LeftId = source.LeftId
AND target.RightId = source.RightId
WHEN NOT MATCHED THEN
INSERT (LeftId, RightId, CustomValue)
VALUES (source.LeftId, source.RightId, source.CustomValue)
WHEN MATCHED THEN
UPDATE SET
CustomValue = source.CustomValue;
MERGE
ステートメントで(ぎこちなく?)MERGE @Mappings AS target
USING (SELECT LeftId, RightId, CustomValue FROM @Values)
AS source (LeftId, RightId, CustomValue)
ON target.LeftId = source.LeftId
AND target.RightId = source.RightId
WHEN NOT MATCHED THEN
INSERT (LeftId, RightId, CustomValue)
VALUES (source.LeftId, source.RightId, source.CustomValue)
WHEN MATCHED THEN
UPDATE SET
CustomValue = source.CustomValue;
WHEN NOT MATCHED BY source
AND EXISTS(SELECT * FROM @Values M WHERE M.LeftId = target.LeftId) THEN
DELETE;
SELECT * FROM @Mappings ORDER BY LeftId, RightId