11

テーブルOrderLines(OrderID int、LineIndex int、)と、1つのオーダーの新しいオーダーラインを定義する同じ構造のテーブル値パラメーターがあります。

だから私が次のOrderLinesを持っていたら

1000   1   bread
1000   2   milk
1001   1   oil
1001   2   yogurt
1002   1   beef
1002   2   pork

および次のTVP

1001   1   yogurt

次のOrderLinesを取得したい

1000   1   bread
1000   2   milk
1001   1   yogurt
1002   1   beef
1002   2   pork

つまり、1つの注文に対してのみ行をタッチします。

だから私はこのように私のクエリを書きました

MERGE
    [OrderLines] AS [Target]
USING
(
    SELECT
        [OrderID], [LineIndex], [Data]
    FROM
        @OrderLines
)
AS [Source] ([OrderID], [LineIndex], [Data])
ON ([Target].[OrderID] = [Source].[OrderID]) AND ([Target].[LineIndex] = [Source].[LineIndex])
WHEN MATCHED THEN
    UPDATE
    SET
        [Target].[Data] = [Source].[Data]
WHEN NOT MATCHED BY TARGET THEN
    INSERT
        ([OrderID], [LineIndex], [Data])
    VALUES
        ([Source].[OrderID], [Source].[LineIndex], [Source].[Data])
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;

そして、他の注文の他のすべての(言及されていない)OrderLinesを削除します。

私は試した

WHEN NOT MATCHED BY SOURCE AND ([Target].[OrderID] = [Source].[OrderID]) THEN

しかし、構文エラーが発生しました。

クエリをどのように書き直す必要がありますか?

4

2 に答える 2

13

の関連するサブセットをOrderLinesターゲットとして使用するだけです。

WITH AffectedOrderLines AS (
    SELECT *
    FROM OrderLines
    WHERE OrderID IN (SELECT OrderID FROM @OrderLines)
)
MERGE
    AffectedOrderLines AS [Target]
USING
(
    SELECT
        [OrderID], [LineIndex], [Data]
    FROM
        @OrderLines
)
AS [Source] ([OrderID], [LineIndex], [Data])
ON ([Target].[OrderID] = [Source].[OrderID]) AND ([Target].[LineIndex] = [Source].[LineIndex])
WHEN MATCHED THEN
    UPDATE
    SET
        [Target].[Data] = [Source].[Data]
WHEN NOT MATCHED BY TARGET THEN
    INSERT
        ([OrderID], [LineIndex], [Data])
    VALUES
        ([Source].[OrderID], [Source].[LineIndex], [Source].[Data])
WHEN NOT MATCHED BY SOURCE THEN
    DELETE;

そして、これがテストするSQLフィドルです。

于 2012-07-05T07:08:50.350 に答える
1

WHEN NOT MATCHED BY SOURCE手始めに、追加のマージ条件で使用できるのは、ターゲットテーブルの列のみです( MSDNにあります)。

また、ソース内のどのエントリとも一致しないため、ターゲットテーブルから余分なエントリをすべて失うのは正常なことだと思います。

WHEN NOT MATCHED BY SOURCE最初に句を削除してから、余分な行や不要な行を個別に削除して、クエリを書き直す必要があります。

次に、以下を追加して、ターゲットテーブルで更新または挿入されたすべてのエントリを取得する必要があります。

DECLARE @OutputTable table( OrderId INT, OrderLine INT);

...Your entire MERGE
WHEN NOT MATCHED BY TARGET THEN
    INSERT
        ([OrderID], [LineIndex], [Data])
    VALUES
        ([Source].[OrderID], [Source].[LineIndex], [Source].[Data])
OUTPUT INSERTED.OrderId, INSERTED.LineIndex INTO @OutputTable

これで@OutputTable、更新されたか、ターゲットテーブルに入力されたすべてのキーがあります(OUTPUT句に注意してください)。

ここで、ターゲットテーブルのどの行がMERGE`ステートメントに含ま@OrderLinesれていないかを確認する必要があります。@OutputTable' and delete them (so they haven't been updated nor inserted by the

DELETE A
FROM [OrderLines] AS A
INNER JOIN @OrderLines AS B
 ON B.OrderId = A.OrderId AND B.LineIndex = A.LineIndex
LEFT OUTER JOIN @OutputTable AS C
 ON C.OrderId = A.OrderId AND C.OrderLine = A.LineIndex
WHERE C.OrderId IS NULL AND C.OrderLine IS NULL 

ここで行っていること(正しいと思います)は、実際には最初に削除したかったことです。内部結合は結果セットをフィルタリングし@OrderLines(したがって、これらのキーを持つ行のみ)、左側の結合はwhere句とともに反半結合を実行し、MERGEステートメントの影響を受けないターゲットテーブルの行を取得します(挿入または更新) 。ただし、ソーステーブル()にあるキーはまだあります@OrderLines

正しいはずです...あなたがそれをテストした後、私に知らせてください。

このアプローチを採用する場合は、これらすべて(MERGE+ DELETE)をトランザクション内にラップすることをお勧めします。

于 2012-07-04T17:36:46.910 に答える