2010-02-16 3 views
27

У меня возник вопрос о производительности SQL Server.T-SQL Вставка или обновление

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

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

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

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

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

Первый:

update persons 
    set [email protected]_id, [email protected]_name, [email protected]_surname 
where [email protected]_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 [email protected]_id, [email protected]_name, [email protected]_surname 
    where [email protected]_id 
else 
    insert into persons(id, name, surname) 
    values (@p_id, @p_name, @p_surname) 

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

+1

не уверен, но я бы просто сделать, если ((COUNT (*))> 0), то обновление для второго варианта –

ответ

7

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

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

11

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

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

4

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

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

2

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

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

update persons 
set [email protected]_name, [email protected]_surname 
where id = @p_id 

commit 

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

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

Edit: очистка

+1

Элегантный, но вы должны также вставить значение для каждого, не нулевой поле в таблице. –

+0

Правда. Я стараюсь избегать любого функционального использования «не null». Приложение должно быть довольно загруженным для загрузки нулей для важных полей. – Patrick

+0

Моим окончательным решением в конце было сделать логику в PHP, для 'put' - попробовать полную вставку, и если она завершилась с' $ sqlErrors [0] [1] == 2627', я делаю полное обновление – Patrick

0

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

UPDATE MyTable 
     SET SomeData = 'Some Data' WHERE ID = 1 
    IF @@ROWCOUNT = 0 
     BEGIN 
     INSERT MyTable 
     SELECT 1, 'Some Data'  
     END 
Смежные вопросы