繁体   English   中英

合并语句插入而不是在SQL Server中更新

[英]Merge statement inserting instead of updating in SQL Server

我正在使用SQL Server 2008,并且试图从暂存(源)表中加载新的(目标)表。 目标表为空。

我认为由于我的目标表为空,因此MERGE语句跳过WHEN MATCHED部分,即INNER JOIN的结果为NULL,因此没有任何更新,它只是继续进行到WHEN NOT MATCHED BY TARGET部分(LEFT OUTER JOIN),然后全部插入登台表中的记录。

我的目标表看起来与我的登台表(行1和行4)完全相似。 目标表中应该只有3行(第4行有3个插入和1个更新)。 因此,我不确定发生了什么。

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


QUERY

MERGE [AccountSettings] AS tgt -- RIGHT TABLE
USING
(
SELECT * FROM [AccountSettings_Staging]
) AS src -- LEFT TABLE
ON src.client_id = tgt.client_id
AND src.account_name = tgt.account_name
WHEN MATCHED -- INNER JOIN
    THEN UPDATE
       SET
         tgt.[FileID] = src.[FileID]
        ,tgt.[account_currency] = src.[account_currency]
        ,tgt.[creation_date] = src.[creation_date]
        ,tgt.[last_modified] = src.[last_modified]

WHEN NOT MATCHED BY TARGET  -- left outer join: A row from the source that has no corresponding row in the target
THEN INSERT
    (
        [FileID],   
        [client_id], 
        [account_name],
        [account_currency],
        [creation_date], 
        [last_modified] 
    )
    VALUES
    (
        src.[FileID],   
        src.[client_id], 
        src.[account_name],
        src.[account_currency], 
        src.[creation_date], 
        src.[last_modified]             
    );

由于目标表是空的,因此在我看来,使用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;

如果您在最新的last_modified上可能具有平局,则需要找到另一个平局决胜者(从示例数据中不明显)。

对于将来的版本,我会说先运行UPDATE

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语法,潜在的错误等。

我认为由于目标表为空,因此MERGE语句会跳过WHEN MATCHED部分

是的,这是正确的,但这是设计MERGE不是“渐进式”合并。 它不会逐行查看是否现在应该更新作为MERGE一部分插入的记录。 它根据是否在目标中找到匹配项,以“批次”方式处理源。

在尝试MERGE之前,您需要在源头处理“重复”记录。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM