T-SQL Вставить или обновить - PullRequest
       25

T-SQL Вставить или обновить

32 голосов
/ 16 февраля 2010

У меня есть вопрос относительно производительности SQL Server.

Предположим, у меня есть таблица persons со следующими столбцами: id, name, surname.

Теперь я хочу вставить новую строку в эту таблицу. Правило следующее:

  1. Если id отсутствует в таблице, вставьте строку.

  2. Если присутствует id, обновить.

У меня есть два решения:

Первый:

update persons
  set id=@p_id, name=@p_name, surname=@p_surname
where id=@p_id
if @@ROWCOUNT = 0 
  insert into persons(id, name, surname)
  values (@p_id, @p_name, @p_surname)

Второе:

if exists (select id from persons where id = @p_id)
  update persons
    set id=@p_id, name=@p_name, surname=@p_surname
  where id=@p_id
else
  insert into persons(id, name, surname)
  values (@p_id, @p_name, @p_surname)

Какой подход лучше? Похоже, что во втором варианте, чтобы обновить строку, ее нужно искать два раза, тогда как в первом варианте - только один раз. Есть ли другие варианты решения проблемы? Я использую MS SQL 2000.

Ответы [ 5 ]

12 голосов
/ 16 февраля 2010

Вариант 1 кажется хорошим. Однако, если вы используете SQL Server 2008, вы также можете использовать MERGE , что может хорошо работать для таких задач UPSERT.

Обратите внимание, что вы можете использовать явную транзакцию и параметр XACT_ABORT для таких задач, так что согласованность транзакций сохраняется в случае проблемы или одновременного изменения.

8 голосов
/ 17 февраля 2010

Оба работают нормально, но я обычно использую вариант 2 (pre-mssql 2008), так как он читается немного яснее.Я бы не стал подчеркивать производительность здесь ... Если это станет проблемой, вы можете использовать NOLOCK в предложении exists.Хотя прежде чем вы начнете использовать NOLOCK повсюду, убедитесь, что вы охватили все свои базы (индексы и общую архитектуру изображений).Если вы знаете, что будете обновлять каждый элемент более одного раза, то стоит рассмотреть вариант 1.

Вариант 3 - не использовать разрушительные обновления.Требуется больше работы, но в основном вы вставляете новую строку каждый раз при изменении данных (никогда не обновляете и не удаляете из таблицы) и получаете представление, которое выбирает все самые последние строки.Это полезно, если вы хотите, чтобы таблица содержала историю всех своих предыдущих состояний, но она также может быть излишней.

4 голосов
/ 17 февраля 2010

Я склонен использовать вариант 1. Если в таблице есть запись, вы сохраняете один поиск. Если нет, вы ничего не потеряете. Более того, во втором варианте вы можете столкнуться с забавными проблемами блокировки и взаимоблокировки, связанными с несовместимостью блокировок. Еще немного информации в моем блоге:

http://sqlblogcasts.com/blogs/piotr_rodak/archive/2010/01/04/updlock-holdlock-and-deadlocks.aspx

3 голосов
/ 19 октября 2016

Стремясь быть немного более сухим, я избегаю выписывать список значений дважды.

begin tran
insert into persons (id)
select @p_id from persons
 where not exists (select * from persons where id = @p_id)

update persons
set name=@p_name, surname=@p_surname
where id = @p_id

commit

Столбцы name и surname должны быть обнуляемыми.

Транзакция означает, что никакой другой пользователь никогда не увидит «пустую» запись.

Редактировать: очистка

2 голосов
/ 09 мая 2017

Вы можете просто использовать @@ RowCount, чтобы увидеть, что обновление сделало что-нибудь. Что-то вроде:

    UPDATE MyTable
       SET SomeData = 'Some Data' WHERE ID = 1
    IF @@ROWCOUNT = 0
      BEGIN
        INSERT MyTable
        SELECT 1, 'Some Data'       
      END
...