Обновить, если отличается / изменилось - PullRequest
29 голосов
/ 13 июля 2011

Можно ли выполнить оператор обновления в sql, но обновлять только в том случае, если обновления отличаются?

например

если в базе данных, col1 = "hello"

update table1 set col1 = 'hello'

не должен выполнять какие-либо обновления

однако, если

update table1 set col1 = "bye"

этот должен выполнить обновление.

Ответы [ 10 ]

25 голосов
/ 13 июля 2011

Идея в том, чтобы не выполнять обновление, если новое значение совпадает с текущим значением в БД.

WHERE col1 != @newValue

(очевидно, также должно быть поле Id для идентификации строки)

WHERE Id = @Id AND col1 != @newValue

PS: Изначально вы хотите выполнять обновление только в том случае, если значение «пока», поэтому просто добавьте AND col1 = 'bye', но я чувствую, что это избыточно, я просто полагаю

18 голосов
/ 04 апреля 2016

Во время компиляции и выполнения запроса SQL Server не тратит время на выяснение того, изменит ли оператор UPDATE какие-либо значения или нет. Он просто выполняет запись, как ожидалось, даже если в этом нет необходимости.

В сценарии типа

update table1 set col1 = 'hello'

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

Если мы хотим избежать ненужных затрат на эти записи, когда в этом нет необходимости, мы должны разработать способ проверки необходимости обновления. Один из способов проверить необходимость обновления - добавить что-то вроде «where col <> 'hello'.

update table1 set col1 = 'hello' where col1 <> 'hello'

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

Но есть гораздо лучшая альтернатива, использующая сочетание предложения EXISTS с предложением EXCEPT. Идея состоит в том, чтобы сравнить значения в строке назначения со значениями в соответствующей исходной строке, чтобы определить, действительно ли необходимо обновление. Посмотрите на измененный запрос ниже и изучите дополнительный фильтр запросов, начинающийся с EXISTS. Обратите внимание, что внутри предложения EXISTS операторы SELECT не имеют предложения FROM. Эта часть особенно важна, потому что это только добавляет дополнительное постоянное сканирование и операцию фильтрации в плане запроса (стоимость обоих тривиальна). Таким образом, в конечном итоге вы получаете очень легкий метод определения того, требуется ли вообще ОБНОВЛЕНИЕ, во-первых, избегая ненужных накладных расходов на запись.

update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists 
    (
    /* DESTINATION */
    select table1.col1
    except
    /* SOURCE */
    select col1 = 'hello'
    )

Это выглядит слишком сложно по сравнению с проверкой обновлений в простом предложении WHERE для простого scenerio в исходном вопросе, когда вы обновляете одно значение для всех строк в таблице с литеральным значением. Однако этот метод работает очень хорошо, если вы обновляете несколько столбцов в таблице, а источником вашего обновления является другой запрос, и вы хотите минимизировать записи и записи в журналах транзакций. Он также работает лучше, чем тестирование каждого поля с помощью <>.

Более полным примером может быть

update table1
   set col1 = 'hello',
       col2 = 'hello',
       col3 = 'hello'
/* Only update rows from CustomerId 100, 101, 102 & 103 */
where table1.CustomerId IN (100, 101, 102, 103)
/* AVOID NET ZERO CHANGES */
  and exists 
    (
    /* DESTINATION */
    select table1.col1
           table1.col2
           table1.col3
    except
    /* SOURCE */
    select z.col1,
           z.col2,
           z.col3
      from #anytemptableorsubquery z
     where z.CustomerId = table1.CustomerId
    )
8 голосов
/ 13 июля 2011

Если вы хотите изменить поле на 'hello', только если оно 'bye', используйте это:

UPDATE table1
SET col1 = 'hello'
WHERE col1 = 'bye'

Если вы хотите обновить только если оно отличается от 'hello', используйте:

UPDATE table1
SET col1 = 'hello'
WHERE col1 <> 'hello'

Есть ли причина для такого странного подхода? Как прокомментировал Даниэль, особого усиления нет - разве что если у вас есть тысячи строк с col1='hello'. Это тот случай?

7 голосов
/ 13 июля 2011

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

  1. Производительность: здесь нет прироста производительности, потому что обновление не только должно найти правильную строку, но и дополнительно сравнить данные.
  2. Триггер: если вы хотите, чтобы триггер срабатывал только при наличии реальных изменений, вам нужно реализовать свой триггер так, чтобы он сравнивал все старые значения с новыми, прежде чем что-либо делать.
3 голосов
/ 13 июля 2011
CREATE OR REPLACE PROCEDURE stackoverflow([your_value] IN TYPE) AS
BEGIN
   UPDATE   [your_table] t
     SET t.[your_collumn] = [your_value]
   WHERE t.[your_collumn] != [your_value];
  COMMIT;


EXCEPTION
 [YOUR_EXCEPTION];

END stackoverflow;
1 голос
/ 09 ноября 2017

Старый вопрос, но ни один из ответов правильно не адресует null значения.

Использование <> или! = Создаст вам проблемы при сравнении значений для различий, если в новом или старом есть потенциальный нользначение для безопасного обновления только при изменении, используйте оператор is distinct from в Postgres.Подробнее об этом здесь

1 голос
/ 18 июля 2017

Вам нужен уникальный ключ id в вашей таблице (предположим, что его значение равно 1), чтобы сделать что-то вроде:

UPDATE table1 SET col1="hello" WHERE id=1 AND col1!="hello"
0 голосов
/ 27 февраля 2017

Я думаю, это должно помочь тебе ...

create trigger [trigger_name] on [table_name]
for insert 
AS declare  @new_val datatype,@id int;
select @new_val = i.column_name from inserted i;
select @id = i.Id from inserted i;
update table_name set column_name = @new_val
where table_name.Id = @id and column_name != @new_val;
0 голосов
/ 13 июля 2011

без объявления переменной или аналога, трудно сослаться на вводимое жестко закодированное значение.Если это ваша цель, вы должны рассмотреть решение Даниэля Хилгарта.Обычно вы будете иметь значение под рукой.В таком случае, вот как вы могли бы это сделать.

declare @t table (col1 varchar(10))

insert @t values ('hello')
insert @t values ('bye')

declare @newvalueforallrows varchar(10)
set @newvalueforallrows = 'hello'

update @t set col1 = @newvalueforallrows
where col1 <> @newvalueforallrows

-- 1 row updated
0 голосов
/ 13 июля 2011
update table1 set col1 = 'bye'
WHERE col1 != 'hello'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...