我有表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
但是出现了语法错误。
我应该如何重写我的查询?
答案 0 :(得分:12)
只需使用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;
并here's a SQL Fiddle进行测试。
答案 1 :(得分: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 the
MERGE`语句中的行:
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子句一起进行反半连接,以获取目标表中不受{的影响的行{1}}声明(插入或更新)但仍有源源表(MERGE
)。
应该是对的...在你测试之后告诉我。
如果您决定采用这种方法,您可能希望将所有这些(@OrderLines
+ MERGE
)包含在事务中。