Как вставить или обновить с помощью одного запроса?

26

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

ввод id = 30122 и имя = Джон

если есть записи с идентификатором 30122, я обновляю столбец имени до john, если нет записей, я вставляю новую запись.

Я могу сделать, используя 2 запроса, как

select * from test where id=30122

если у него есть какие-то записи, то я могу использовать update test set name='john' where id=3012

или если у него нет записей, то я могу использовать

insert into test(name) values('john')

Но я хотел использовать один запрос?

Может кто-нибудь сказать, если это возможно?

SpringLearner
источник
1
But I wanted to use single query?Зачем?
Аарон Бертран
@AaronBertrand Мой бэкэнд разработан с использованием java. Так что, если я использую 2 запроса, я должен дважды обратиться к БД. Поэтому, если это можно сделать с помощью одного запроса, зачем использовать 2 запроса
SpringLearner
1
Java не поддерживает хранимую процедуру или один пакет с двумя операторами, требующими только одного попадания в базу данных?
Аарон Бертран
@AaronBertrand Не могли бы вы привести пример того, как бы вы справились с этим с SQL Server 2008 или более поздней версии?
eaglei22
1
@ eaglei22 Я бы использовал второй пример в ответе Виджайпа ниже. Я бы все равно не выбрал MERGEни в одной версии, даже в SQL Server 2019. Некоторые предыстории этого здесь .
Аарон Бертран

Ответы:

41

Вы можете попробовать это

IF EXISTS(select * from test where id=30122)
   update test set name='john' where id=3012
ELSE
   insert into test(name) values('john');

Другой подход для повышения производительности

update test set name='john' where id=3012
IF @@ROWCOUNT=0
   insert into test(name) values('john');

а также читать эту вредную привычку, чтобы пнуть на префиксе схемы

vijayp
источник
4
Первый пример является расточительным и часто может привести к тупикам - я бы вообще не советовал.
Аарон Бертран
@AaronBertrand хочешь разработать? Спасибо
Hexo
5
@SlapY Конечно, в первом примере вы говорите: «Эй, SQL Server, есть строка с этим идентификатором?» SQL Server уходит, чтобы найти строку, возможно, с помощью сканирования, а затем возвращается с ответом. «Почему, да, пользователь, у меня есть строка с этим идентификатором!» Тогда вы говорите: «Хорошо, SQL Server, идут найти эту строку еще раз , но на этот раз, обновить его!» Вы видите, как дважды выполнять поиск или сканирование? Можете ли вы представить себе, что произойдет, если другой пользователь задаст SQL Server тот же вопрос о существовании строки, прежде чем вы приступите к решению этой проблемы?
Аарон Бертран
Спасибо, я просто не понимаю, почему первый грозит тупиком, а второй нет? Оба состоят из нескольких операторов, которые могут быть перехвачены, если они не выполняются с полной блокировкой. Я ошибся?
Hexo
2
@ 0x25b3 Дело не в том, что одному угрожают тупики, а другому нет, дело в том, что первый пример гораздо более подвержен им. В любом случае вы должны заключать полную и правильную транзакцию, но люди этого не делают, так что ...
Аарон Бертран
17

Предполагая SQL Server 2008 или более позднюю версию, вы можете использовать MERGE:

Стол

CREATE TABLE dbo.Test
(
    id integer NOT NULL,
    name varchar(30) NULL,

    CONSTRAINT PK_dbo_Test__id
        PRIMARY KEY CLUSTERED (id)
);

запрос

MERGE dbo.Test WITH (SERIALIZABLE) AS T
USING (VALUES (3012, 'john')) AS U (id, name)
    ON U.id = T.id
WHEN MATCHED THEN 
    UPDATE SET T.name = U.name
WHEN NOT MATCHED THEN
    INSERT (id, name) 
    VALUES (U.id, U.name);

SERIALIZABLEПодсказка необходима для правильной работы под высоким параллелизмом .

Вы можете найти сравнения распространенных методов Майкла Дж. Сварта здесь:

Mythbusting: одновременное обновление / вставка решений

Эвалдас Буйнаускас
источник
8
Слияние имеет некоторые проблемы .
vonPryz
таинственная ссылка там превосходна. Хороший!
JonnyRaa