【问题标题】:SQL MERGE: Is it possible to ignore a MATCH?SQL MERGE:是否可以忽略 MATCH?
【发布时间】:2019-03-06 18:30:08
【问题描述】:

这是我的合并声明:

MERGE PE_TranslationPhrase T
USING PE_TranslationPhrase_Staging S 
ON (T.CultureName = S.CultureName AND T.Phrase = S.Phrase)
WHEN MATCHED 
        THEN UPDATE SET T.TranslationId = T.TranslationId -- do nothing
WHEN NOT MATCHED BY TARGET
        THEN INSERT (TranslationId, CultureName, Phrase)
        VALUES (S.TranslationId, S.CultureName, S.Phrase);

WHEN MATCHED 部分似乎有问题。基于on this question and answer,我将WHEN MATCHED改为:

THEN UPDATE SET T.TranslationId = T.TranslationId

但我仍然收到此错误:

MERGE 语句多次尝试更新或删除同一行。当目标行匹配多个源行时会发生这种情况。 MERGE 语句不能多次 UPDATE/DELETE 目标表的同一行。优化 ON 子句以确保目标行最多匹配一个源行,或使用 GROUP BY 子句对源行进行分组。

我知道原因了。根据ON 声明,我得到了重复的匹配项。但是……我不在乎。如果源表中的记录已经存在于目标表中,那么我什么都不做。根本不更新任何东西。跳过它。

这可能吗?

【问题讨论】:

  • 为什么不直接省略WHEN MATCHED 部分?
  • 值得一提的是,您不能在 NULL 上进行匹配,因此您最好在两个匹配列周围使用 ISNULL(如果它们还不是 NOT NULL)

标签: sql sql-server tsql merge sql-server-2017


【解决方案1】:

哦。原来你不需要'WHEN MATCHED'语句。我认为这是必需的。

MERGE PE_TranslationPhrase T
USING PE_TranslationPhrase_Staging S 
ON (T.CultureName = S.CultureName AND T.Phrase = S.Phrase)
WHEN NOT MATCHED BY TARGET
        THEN INSERT (TranslationId, CultureName, Phrase)
        VALUES (S.TranslationId, S.CultureName, S.Phrase);

【讨论】:

  • 值得一提的是,你不能在 NULL 上进行匹配,所以我会在两个匹配列周围使用 ISNULL,以确保当两个表在匹配列中都有 NULL 值时你不会得到“不匹配”
【解决方案2】:

如果没有进行更新,您是否考虑过使用PE_TranslationPhrase_Staging 中不在PE_TranslationPhrase 中的行进行插入?

CTE 方法:

WITH CTE AS (
SELECT 
    S.TranslationId, 
    S.CultureName, 
    S.Phrase
FROM PE_TranslationPhrase_Staging S 
LEFT JOIN PE_TranslationPhrase T on S.Phrase = T.Phrase and S.CultureName = T.CultureName
WHERE T.PHRASE IS NULL
)

INSERT INTO PE_TranslationPhrase (TranslationId, CultureName, Phrase)
SELECT 
    TranslationId, 
    CultureName, 
    Phrase
FROM CTE

子查询方法:

INSERT INTO PE_TranslationPhrase (TranslationId, CultureName, Phrase)
SELECT 
TW.TranslationId, 
TW.CultureName, 
TW.Phrase
FROM (
SELECT 
    S.TranslationId, 
    S.CultureName, 
    S.Phrase
FROM PE_TranslationPhrase_Staging S 
LEFT JOIN PE_TranslationPhrase T on S.Phrase = T.Phrase and S.CultureName = T.CultureName
WHERE T.PHRASE IS NULL ) TW

【讨论】:

  • 与合并相比,速度/成本是多少?
  • 合并存在多个问题,其中一些与死锁有关,这可能不是问题,因为只完成了插入。以下链接进一步概述了这些。至于性能,我建议对两者进行测试,然后查看执行计划以了解使用了哪些逻辑/物理运算符、索引等以及以何种方式使用。子查询实际上可能会执行得更好,为了清楚起见,我在这里使用了 CTE。 mssqltips.com/sqlservertip/3074/…
  • 我相信你对子查询很熟悉,但我更新了我的帖子,并举了一个例子。另请注意,子查询必须是别名(上面的 TW)
【解决方案3】:

很遗憾,不能在 MERGE 语句中省略此错误。但是我已经通过生成一个标识列(不是持久的)来解决这个问题,它允许我为句子的source 和target 的每条记录添加一个唯一值并比较表格。像这样的:

ROW_NUMBER () OVER (PARTITION BY CultureName, Phrase ORDER BY CultureName, Phrase)

【讨论】:

    【解决方案4】:

    不,您不能在有多个匹配项的情况下使用Merge ... When Matched ...,并且您可以发现,您可以简单地删除When Matched 部分。但是,如果您需要 update 的此部分,而不是使用 Merge,您可以使用以下代码:

    update PE_TranslationPhrase
    set  TranslationId = S.xxx
    from
    PE_TranslationPhrase T, PE_TranslationPhrase_Staging S
    where T.CultureName = S.CultureName AND T.Phrase = S.Phrase
    

    【讨论】:

      猜你喜欢
      • 2010-10-26
      • 1970-01-01
      • 2023-01-30
      • 2016-08-10
      • 2021-10-01
      • 1970-01-01
      • 2014-11-26
      相关资源
      最近更新 更多