当条件匹配新插入的记录时,T-Sql MERGE 语句更新

Posted

技术标签:

【中文标题】当条件匹配新插入的记录时,T-Sql MERGE 语句更新【英文标题】:T-Sql MERGE statement update when criteria matches newly inserted record 【发布时间】:2016-04-21 13:53:16 【问题描述】:

这是我拥有的 XML:

<?xml version="1.0" encoding="UTF-8"?>
<Data>
   <Record>
      <ServerId>1</ServerId>
      <CompanyId>1</CompanyId>
      <InstanceId>2</InstanceId>
      <TemplateId>23</TemplateId>
      <ContactId>11052</ContactId>
      <RecordId>11462</RecordId>
      <TaskId>677</TaskId>
      <EntryDate>2016-04-21 14:17:02:813</EntryDate>
      <EntryKey>key_test_1</EntryKey>
      <EntryValue>value_test_1</EntryValue>
   </Record>
   <Record>
      <ServerId>1</ServerId>
      <CompanyId>1</CompanyId>
      <InstanceId>2</InstanceId>
      <TemplateId>23</TemplateId>
      <ContactId>11052</ContactId>
      <RecordId>11462</RecordId>
      <TaskId>677</TaskId>
      <EntryDate>2016-04-21 14:17:02:873</EntryDate>
      <EntryKey>key_test_2</EntryKey>
      <EntryValue>value_test_2</EntryValue>
   </Record>
   <Record>
      <ServerId>1</ServerId>
      <CompanyId>1</CompanyId>
      <InstanceId>2</InstanceId>
      <TemplateId>23</TemplateId>
      <ContactId>11052</ContactId>
      <RecordId>11462</RecordId>
      <TaskId>677</TaskId>
      <EntryDate>2016-04-21 14:17:02:935</EntryDate>
      <EntryKey>key_test_1</EntryKey>
      <EntryValue>value_test_3</EntryValue>
   </Record>
</Data>

这是将这些数据插入或更新到单个表中的 MERGE 语句:

merge WRF_REPOSITORY_CUSTOM_DATA rep
using (select 
        entity.value('ServerId[1]', 'int') as ServerId,
        entity.value('CompanyId[1]', 'int') as CompanyId,
        entity.value('InstanceId[1]', 'int') as InstanceId,
        entity.value('TemplateId[1]', 'int') as TemplateId,
        entity.value('ContactId[1]', 'bigint') as ContactId,
        entity.value('RecordId[1]', 'bigint') as RecordId,
        entity.value('TaskId[1]', 'bigint') as TaskId,
        entity.value('EntryDate[1]', 'datetime') as EntryDate,
        entity.value('EntryKey[1]', 'varchar(max)') as EntryKey,
        entity.value('EntryValue[1]', 'varchar(max)') as EntryValue
        from @xmlInsertOrReplace.nodes('/Data/Record') as T(entity)) as dat
on 
    rep.ServerId = dat.ServerId and 
    rep.CompanyId = dat.CompanyId and
    rep.InstanceId = dat.InstanceId and
    rep.TemplateId = dat.TemplateId and
    rep.ContactId = dat.ContactId and 
    rep.RecordId = dat.RecordId and
    rep.EntryKey = dat.EntryKey
when MATCHED then update set
    rep.TaskId = dat.TaskId,
    rep.EntryDate = dat.EntryDate,
    rep.EntryValue = dat.EntryValue
when NOT MATCHED then
    insert (ServerId, CompanyId, InstanceId, TemplateId, ContactId, RecordId, TaskId, EntryDate, EntryKey, EntryValue)
    values (dat.ServerId, dat.CompanyId, dat.InstanceId, dat.TemplateId, dat.ContactId, dat.RecordId, dat.TaskId, dat.EntryDate, dat.EntryKey, dat.EntryValue);

当表中没有记录时,我期望的是第一条 xml 记录将被插入,第二条 xml 记录将被插入,第三条 xml 记录将更新第一个表记录,因为它符合条件。实际发生的是我得到 3 个插入而不是 2 个插入和 1 个更新。

结果截图:

有没有办法在每次插入或更新后强制 MERGE 语句执行 COMMIT 或其他操作?我不想对 xml 记录进行分组或选择最大/最后一个。

更新:

我使用了一个类似于第一个的合并操作。唯一的区别是这个连接了值。

when MATCHED then update set
    rep.TaskId = dat.TaskId,
    rep.EntryDate = dat.EntryDate,
    rep.EntryValue = case len(isnull(rep.EntryValue, '')) when 0 then dat.EntryValue else rep.EntryValue + ';' + dat.EntryValue end 

而预期的结果应该是:

key_test_1 | value_test_1;value_test_3
key_test_2 | value_test_2

【问题讨论】:

没有。 MERGE 是一个单一的语句。如果你想先插入然后更新,你需要使用两个单独的语句。 @Sean Lange 是正确的。 MERGE 语句将现有表中的数据与解析的 XML 数据中的数据进行比较。 “我不想对 xml 记录进行分组或选择最大/最后一个。” - 为什么不呢?这似乎是解决方案,如果所有字段都将被第三条记录覆盖,为什么还要插入第一条记录。 @GarethD:因为我还有一个合并语句可以执行相同的操作,但它将值与现有的值连接起来。因此,相同的 xml 数据,插入、插入、更新(将第 3 条 xml 记录值连接到新插入的第 1 条表记录)。这可以同时在同一台机器上的多个应用程序中完成...... 顺便说一句,您知道"The MERGE statement cannot update the same row more than once"(尝试这样做会返回错误)吗?在进行 MERGE 之前,您需要对源行进行分组/连接。 【参考方案1】:

你可以试试这个:

使用ROW_NUMBER 在您的 XML 中查找最新记录并忽略较旧的记录,无论如何它都会被覆盖...

WITH shreddedXML AS
(
    select 
        entity.value('ServerId[1]', 'int') as ServerId,
        entity.value('CompanyId[1]', 'int') as CompanyId,
        entity.value('InstanceId[1]', 'int') as InstanceId,
        entity.value('TemplateId[1]', 'int') as TemplateId,
        entity.value('ContactId[1]', 'bigint') as ContactId,
        entity.value('RecordId[1]', 'bigint') as RecordId,
        entity.value('TaskId[1]', 'bigint') as TaskId,
        entity.value('EntryDate[1]', 'datetime') as EntryDate,
        entity.value('EntryKey[1]', 'varchar(max)') as EntryKey,
        entity.value('EntryValue[1]', 'varchar(max)') as EntryValue
    from @xmlInsertOrReplace.nodes('/Data/Record') as T(entity)
)
,SearchForMostActualRows AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY ServerId,CompanyId,InstanceId,TemplateId,ContactId,RecordId,EntryKey ORDER BY EntryDate DESC) AS Nr
          ,*
    FROM shreddedXML
)
SELECT * 
FROM SearchForMostActualRows
WHERE Nr=1

现在用这个做你的MERGE...

【讨论】:

问题是我不能忽略任何记录。我会用原因更新我的问题。 @HABJAN 我刚刚读到了原因。只需将我的查询更改为SELECT * INTO #tempTable。在下一步中,您使用SELECT * FROM #tempTable WHERE Nr=1 执行MERGE,之后您可以使用SELECT * FROM #tempTable WHERE Nr&gt;1 做任何您想做的事情 即使这并不能解决我的问题,它为我指明了正确的方向。谢谢。

以上是关于当条件匹配新插入的记录时,T-Sql MERGE 语句更新的主要内容,如果未能解决你的问题,请参考以下文章

没有连接条件的 T-SQL 匹配记录 1 到 1

MERGE - 有条件的“当匹配然后更新”

merge into using 详解

MERGE语法详解

merge into语句的使用

为啥 MERGE 语句会抛出唯一键约束错误