在SQL Server中插入而不是更新合并语句
我正在使用SQLServer2008,我正在尝试从一个暂存(源)表加载一个新的(目标)表。目标表为空 我认为,由于我的目标表是空的,MERGE语句跳过了WHEN MATCHED部分,即内部连接的结果为NULL,因此没有任何内容更新,它只是继续执行WHEN NOT MATCHED BY target部分(左外部连接),插入staging表中的所有记录 我的目标表看起来与暂存表(第1行和第4行)完全相似。目标表中应该只有3行(3次插入,第4行更新一次)。所以,我不知道发生了什么 FileID client_id account_name account_currency creation_date last_modified 210 12345 Cars USD 2013-11-21 2013-11-27 211 23498 Truck USD 2013-09-22 2013-11-27 212 97652 Cars - 1 USD 2013-09-17 2013-11-27 210 12345 Cars JPY 2013-11-21 2013-11-29 我认为,因为我的目标表是空的,所以MERGE语句跳过了WHEN MATCHED部分 好吧,这是正确的,但这是出于设计——在SQL Server中插入而不是更新合并语句,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我正在使用SQLServer2008,我正在尝试从一个暂存(源)表加载一个新的(目标)表。目标表为空 我认为,由于我的目标表是空的,MERGE语句跳过了WHEN MATCHED部分,即内部连接的结果为NULL,因此没有任何内容更新,它只是继续执行WHEN NOT MATCHED BY target部分(左外部连接),插入staging表中的所有记录 我的目标表看起来与暂存表(第1行和第4行)完全相似。目标表中应该只有3行(3次插入,第4行更新一次)。所以,我不知道发生了什么 FileID cl
MERGE
不是“渐进式”合并。它不会逐行查看作为合并的一部分插入的记录现在是否应该更新。它根据是否在目标中找到匹配项来“批量”处理源
在尝试合并之前,您需要在源位置处理“重复”记录
我认为,因为我的目标表是空的,所以MERGE语句跳过了WHEN MATCHED部分
好吧,这是正确的,但这是出于设计——MERGE
不是“渐进式”合并。它不会逐行查看作为合并的一部分插入的记录现在是否应该更新。它根据是否在目标中找到匹配项来“批量”处理源
在尝试合并之前,您需要在源位置处理“重复”记录,因为目标表是空的,所以在我看来,使用合并
就像雇佣水管工给您倒一杯水一样。而MERGE
对于表的每一行只独立地操作一个分支-它看不到键是重复的,因此执行插入然后执行更新-这表明您认为SQL总是按行操作,而实际上大多数操作都是同时对整个集合执行的
为什么不只插入最近的行:
;WITH cte AS
(
SELECT FileID, ... other columns ...,
rn = ROW_NUMBER() OVER (PARTITION BY FileID ORDER BY last_modified DESC)
FROM dbo.AccountSettings_Staging
)
INSERT dbo.AccountSettings(FileID, ... other columns ...)
SELECT FileID, ... other columns ...
FROM cte WHERE rn = 1;
如果您有可能在最近的上次修改的
上出现平局,您需要找到另一个平局破坏者(从您的示例数据中不明显)
对于未来的版本,我会说首先运行更新
:
UPDATE a SET client_id = s.client_id /* , other columns that can change */
FROM dbo.AccountSettings AS a
INNER JOIN dbo.AccountSettings_Staging AS s
ON a.FileID = s.FileID;
(当然,如果源包含多个具有相同FileID
的行,这将选择任意行-您可能也希望在此处使用CTE以使选择可预测。)
然后将此条款添加到上面的INSERT
CTE中:
FROM dbo.AccountSettings_Staging AS s
WHERE NOT EXISTS (SELECT 1 FROM dbo.AccountSettings
WHERE FileID = s.FileID);
以适当的隔离级别将其全部封装在事务中,您仍然可以避免大量复杂的MERGE
语法、潜在的bug等。由于目标表是空的,在我看来,使用MERGE
就像雇佣一个水管工给您倒一杯水。而MERGE
对于表的每一行只独立地操作一个分支-它看不到键是重复的,因此执行插入然后执行更新-这表明您认为SQL总是按行操作,而实际上大多数操作都是同时对整个集合执行的
为什么不只插入最近的行:
;WITH cte AS
(
SELECT FileID, ... other columns ...,
rn = ROW_NUMBER() OVER (PARTITION BY FileID ORDER BY last_modified DESC)
FROM dbo.AccountSettings_Staging
)
INSERT dbo.AccountSettings(FileID, ... other columns ...)
SELECT FileID, ... other columns ...
FROM cte WHERE rn = 1;
如果您有可能在最近的上次修改的
上出现平局,您需要找到另一个平局破坏者(从您的示例数据中不明显)
对于未来的版本,我会说首先运行更新
:
UPDATE a SET client_id = s.client_id /* , other columns that can change */
FROM dbo.AccountSettings AS a
INNER JOIN dbo.AccountSettings_Staging AS s
ON a.FileID = s.FileID;
(当然,如果源包含多个具有相同FileID
的行,这将选择任意行-您可能也希望在此处使用CTE以使选择可预测。)
然后将此条款添加到上面的INSERT
CTE中:
FROM dbo.AccountSettings_Staging AS s
WHERE NOT EXISTS (SELECT 1 FROM dbo.AccountSettings
WHERE FileID = s.FileID);
以适当的隔离级别将其封装在事务中,您仍然可以避免大量复杂的MERGE
语法、潜在错误等。相关:。@AaronBertrand谢谢。您建议在它的位置使用什么(如果存在的话除外)?它现在是空的,但不会用于将来的作业运行。如果MERGE语句继续为任何帐户插入重复项,则会导致问题。您应该在问题中这样说。问题中的所有内容都让我相信这是某种一次性的事情,或者目标表总是先被清除。相关:。@AaronBertrand谢谢。您建议在它的位置使用什么(如果存在的话除外)?它现在是空的,但不会用于将来的作业运行。如果MERGE语句继续为任何帐户插入重复项,则会导致问题。您应该在问题中这样说。问题中的所有内容都让我相信这是某种一次性的事情,或者目标表总是先被清除。它现在是空的,但不会用于以后的作业运行。如果MERGE语句继续为任何帐户插入重复项,则会导致问题。可能我需要将CTE用作合并中的源,然后在目标表中更新或删除。@仅当源中包含不在目标中的重复时,才会插入重复。我也不相信SQL会按照记录在CTE中出现的顺序更新记录。您需要以任何方式(或使用光标,这将是最后的手段)在源代码中解决复制-Aaron,更新
/插入
在幕后做什么?不反驳你的答案,只是重申MERGE
@DStanley确实没有什么“魔力”,那么为什么要使用完全繁琐和不直观的语法呢?或者去努力验证你没有受到十几个错误结果和其他严重错误的影响,而这些错误还没有被修复?是吗?@AaronBertrand我确实这么做了,我完全同意——我只是重申,MERGE
在UPDATE
/INSERT
b上买不到多少东西