Я написал хранимую процедуру, которая будет выполнять обновление, если запись существует, иначе она выполнит вставку. Это выглядит примерно так:
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
Моя логика написания его таким образом заключается в том, что обновление будет выполнять неявный выбор с использованием предложения where, и если это вернет 0, то произойдет вставка.
Альтернативой этому может быть выбор, а затем в зависимости от количества возвращенных строк либо обновление, либо вставка. Это я считал неэффективным, потому что если вы собираетесь выполнить обновление, это вызовет 2 выбора (первый явный вызов выбора и второй неявный в месте обновления). Если бы процесс делал вставку, то разницы в эффективности не было бы.
Здесь моя логика звучит? Это как бы вы могли объединить вставку и обновление в сохраненную процедуру?
источник
Пожалуйста, прочтите сообщение в моем блоге , чтобы узнать о хорошем и безопасном шаблоне, который вы можете использовать. Есть много соображений, и принятый ответ на этот вопрос далеко не безопасен.
Чтобы быстро ответить, попробуйте следующий шаблон. Он будет нормально работать с SQL 2000 и выше. SQL 2005 предоставляет вам обработку ошибок, которая открывает другие параметры, а SQL 2008 дает вам команду MERGE.
источник
Если будет использоваться с SQL Server 2000/2005, исходный код должен быть заключен в транзакцию, чтобы гарантировать, что данные остаются согласованными в параллельном сценарии.
Это повлечет за собой дополнительные затраты на производительность, но обеспечит целостность данных.
Добавьте, как уже предлагалось, MERGE, если он доступен.
источник
Между прочим, MERGE - одна из новых возможностей SQL Server 2008.
источник
Вам не только нужно запускать его в транзакции, он также требует высокого уровня изоляции. На самом деле уровень изоляции по умолчанию - Read Commited, и этот код требует Serializable.
Может быть, хорошей идеей может быть добавление проверки ошибок @@ и отката.
источник
Если вы не выполняете слияние в SQL 2008, вы должны изменить его на:
если @@ rowcount = 0 и @@ error = 0
в противном случае, если обновление по какой-то причине не удается, оно будет пытаться выполнить вставку позже, потому что количество строк в неудачном операторе равно 0
источник
Большой поклонник UPSERT, действительно сокращает код для управления. Вот еще один способ сделать это: один из входных параметров - это ID, если ID равен NULL или 0, вы знаете, что это INSERT, в противном случае это обновление. Предполагается, что приложение знает, есть ли идентификатор, поэтому не будет работать во всех ситуациях, но сократит выполнение вдвое, если вы это сделаете.
источник
Измененный пост Димы Маленко:
Вы можете перехватить ошибку и отправить запись в таблицу с ошибкой вставки.
Мне нужно было это сделать, потому что мы берем все данные, отправляемые через WSDL, и, если возможно, исправляем их внутри.
источник
Ваша логика кажется здравой, но вы можете подумать о добавлении кода для предотвращения вставки, если вы передали определенный первичный ключ.
В противном случае, если вы всегда выполняете вставку, если обновление не повлияло на какие-либо записи, что произойдет, если кто-то удалит запись до запуска «UPSERT»? Теперь запись, которую вы пытались обновить, не существует, поэтому вместо нее будет создана запись. Вероятно, это не то поведение, которого вы искали.
источник