синтаксис для одной строки MERGE / upsert в SQL Server

Я пытаюсь выполнить вставку / обновление одной строки в таблице, но все примеры предназначены для наборов.

Кто-нибудь может исправить мой синтаксис, пожалуйста:

MERGE member_topic ON mt_member = 0 AND mt_topic = 110
WHEN MATCHED THEN UPDATE SET mt_notes = 'test'
WHEN NOT MATCHED THEN INSERT (mt_member, mt_topic, mt_notes) VALUES (0, 110, 'test')

Разрешение на marc_s состоит в том, чтобы преобразовать одну строку в подзапрос, что заставляет меня думать, что команда MERGE на самом деле не предназначена для вставок одной строки.

MERGE member_topic
USING (SELECT 0 mt_member, 110 mt_topic) as source
ON member_topic.mt_member = source.mt_member AND member_topic.mt_topic = source.mt_topic
WHEN MATCHED THEN UPDATE SET mt_notes = 'test'
WHEN NOT MATCHED THEN INSERT (mt_member, mt_topic, mt_notes) VALUES (0, 110, 'test');

person Jacob    schedule 19.03.2010    source источник
comment
Хм, в моем браузере строки расположены так близко, что некоторые подчеркивания не отображаются.   -  person Jacob    schedule 19.03.2010


Ответы (2)


По сути, вы на правильном пути - но вам не хватает источника, из которого вы хотите объединить данные - попробуйте что-то вроде этого:

MERGE 
   member_topic AS target
USING 
   someOtherTable AS source
ON 
   target.mt_member = source.mt_member 
   AND source.mt_member = 0 
   AND source.mt_topic = 110
WHEN MATCHED THEN 
   UPDATE SET mt_notes = 'test'
WHEN NOT MATCHED THEN 
   INSERT (mt_member, mt_topic, mt_notes) VALUES (0, 110, 'test')
; 

Не существует специального синтаксиса для MERGE одной строки - все, что вам нужно сделать, это использовать правильное предложение. С этим правильным условием в предложении ON вы можете ограничить источник одной строкой - нет проблем.

И не забывайте конечную точку с запятой! Шутка ли - это важно!

См. это сообщение в блоге за действительно хорошее вступление к MERGE.

person marc_s    schedule 19.03.2010
comment
У меня нет someOtherTable. Вы хотите сказать, что мне нужно смоделировать что-то вроде этого? ИСПОЛЬЗОВАНИЕ (ВЫБРАТЬ 0 mt_member, 110 mt_topic) в качестве источника - person Jacob; 19.03.2010
comment
@Jacob: у вас должен быть источник, да - подзапрос, о котором вы упомянули, должен подойти - попробуйте! - person marc_s; 19.03.2010
comment
Для тех, кого смущает этот ответ, ознакомьтесь с другим вопросом . - person Nate Cook; 26.11.2012
comment
@NateCook: что насчет моего ответа сбивает с толку? Я бы хотел его улучшить - если возможно - person marc_s; 26.11.2012
comment
Путаница связана с непониманием основ читателями (включая меня). В вашем ответе нет ничего плохого. Другой вопрос просто расширяет тему для дальнейшего разъяснения. - person Nate Cook; 13.12.2012

Наконец-то я получил синтаксис Upsert, используя MERGE в SQL Server 2008. Используя то, что Джейкоб хотел сделать (Upsert):

IF EXISTS(SELECT * FROM member_topic WHERE mt_member = 0 AND mt_topic = 110)
BEGIN
    --update existing row
    UPDATE member_topic SET mt_notes = 'test'
    WHERE mt_member = 0
    AND mt_topic = 110
END
ELSE
BEGIN
    --insert new row
    INSERT INTO member_topic (mt_member, mt_topic, mt_notes)
    VALUES (0, 110, 'test')
END

Эквивалентный синтаксис MERGE:

MERGE member_topic
USING ( 
    VALUES (0, 110, 'test')
) AS foo (mt_member, mt_topic, mt_notes) 
ON member_topic.mt_member = foo.mt_member 
   AND member_topic.mt_topic = foo.mt_topic
WHEN MATCHED THEN
   UPDATE SET mt_notes = foo.mt_notes
WHEN NOT MATCHED THEN
   INSERT (mt_member, mt_topic, mt_notes)
   VALUES (foo.mt_member, foo.mt_topic, foo.mt_notes)
; --A MERGE statement must be terminated by a semi-colon (;).
person Ian Boyd    schedule 26.06.2012
comment
Почему в предложении WHEN NOT MATCHED THEN вы вставляете VALUES (mt_member, mt_topic, mt_notes), а не VALUES (foo.mt_member, foo.mt_topic, foo.mt_notes)? - person Sam P; 18.10.2013
comment
@SamP Почему бы и нет? Это похоже на то, что SQL Server собирается получать значения из другого места, а не из псевдотаблицы foo? Ой, погоди ... - person Ian Boyd; 31.01.2016
comment
Не будет ли строка UPDATE member_topic SET mt_notes = 'test' в примере без объединения обновлять все строки в таблице? Не то, что было задумано. - person avl_sweden; 13.05.2016
comment
Я обнаружил, что шаблон IF EXISTS / UPDATE / ELSE / INSERT является источником взаимоблокировок и в лучшем случае требует дополнительных операций ввода-вывода (особенно, если EXISTS / UPDATE должен выполнить сканирование). Лучше всего (опять же по моему опыту) просто попробовать обновить и вставить, если никакие строки не были затронуты. UPDATE / IF @@ROWCOUNT = 0 / INSERT. - person Aaron Bertrand; 20.01.2017
comment
IF EXISTS будет удерживать блокировку (и вызывать взаимоблокировку) только в том случае, если вы работаете на уровне изоляции REPEATABLE READ или SERIALIZABLE (т.е. WITH (HOLDLOCK)). Без него: MERGE и Upsert имеют состояние гонки. Вместе с этим: У MERGE и Upset есть шанс зайти в тупик. - person Ian Boyd; 20.01.2017
comment
Да, я написал ту первую статью. Я хотел сказать, что им обоим нужен HOLDLOCK, чтобы избежать состояния гонки (удивлен, что ваш ответ не содержит HOLDLOCK), и что при использовании UPSERT вы должны просто UPDATE/IF @@ROWCOUNT/INSERT вместо того, что люди обычно делают IF EXISTS / UPDATE / ELSE / INSERT. У последнего больше шансов зайти в тупик. - person Aaron Bertrand; 26.01.2017
comment
В любом случае, моя основная причина для комментариев заключается в том, что я продолжаю рекомендовать людям держаться подальше от MERGE и использовать шаблон UPDATE/IF @@ROWCOUNT = 0/INSERT, используя любой механизм блокировки, более подходящий для их среды. - person Aaron Bertrand; 26.01.2017
comment
(Это тоже стоит прочитать.) - person Aaron Bertrand; 27.01.2017