Во время компиляции и выполнения запроса 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
)