Обновление оператора T-Sql MERGE, когда критерии соответствуют вновь вставленной записи

Это 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

person HABJAN    schedule 21.04.2016    source источник
comment
Нет. MERGE — это один оператор. Если вы хотите сначала вставить, а затем обновить, вам нужно использовать два отдельных оператора.   -  person Sean Lange    schedule 21.04.2016
comment
@ Шон Ланге прав. Оператор MERGE сравнивает данные в существующей таблице с данными в проанализированных данных XML.   -  person Steven    schedule 21.04.2016
comment
Я не хочу группировать xml-записи или выбирать максимальную/последнюю. – Почему бы и нет? Это похоже на решение, зачем вообще вставлять первую запись, если все поля все равно будут перезаписаны третьей записью.   -  person GarethD    schedule 21.04.2016
comment
@GarethD: потому что у меня есть еще один оператор слияния, который делает то же самое, но объединяет значение с существующим. Итак, одни и те же данные xml, вставка, вставка, обновление (привязка 3-го значения записи xml к вновь вставленной 1-й записи таблицы). И это можно сделать из нескольких приложений на одном компьютере одновременно...   -  person HABJAN    schedule 21.04.2016
comment
ХАБДЖАН, я только что разместил комментарий под своим ответом, чтобы отразить это...   -  person Shnugo    schedule 21.04.2016
comment
Кстати, знаете ли вы, что оператор MERGE не может обновлять одну и ту же строку более одного раза (попытка сделать это вернет ошибку)? Вам необходимо сгруппировать/объединить исходные строки перед выполнением MERGE.   -  person Vladimir Baranov    schedule 21.04.2016
comment
@VladimirBaranov: Спасибо, что сообщили мне, я не знал об этом. Это заставило меня изменить свою логику.   -  person HABJAN    schedule 22.04.2016


Ответы (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 с этим...

person Shnugo    schedule 21.04.2016
comment
Проблема в том, что я не могу игнорировать ни одну запись. Я обновлю свой вопрос с причиной. - person HABJAN; 21.04.2016
comment
@HABJAN Я только что прочитал причину. Просто измените мой запрос на SELECT * INTO #tempTable. На следующем шаге вы делаете MERGE с SELECT * FROM #tempTable WHERE Nr=1, а после этого делаете все, что хотите, с SELECT * FROM #tempTable WHERE Nr>1 - person Shnugo; 21.04.2016
comment
Даже это не решает мою проблему, это указало мне правильное направление. Спасибо. - person HABJAN; 22.04.2016