11

我有相同结构的表 OrderLines(OrderID int, LineIndex int, ) 和表值参数,为一个订单定义了新的订单行。

所以如果我有以下 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

即只为一个订单触摸行。

所以我这样写了我的查询

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 Fiddle 。

于 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子句)。

您现在只需要查看目标表中的哪些行(仅与 中的键匹配)@OrderLines不在@OutputTable' and delete them (so they haven't been updated nor inserted by theMERGE` 语句中):

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 回答