По време на компилиране и изпълнение на заявка, 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 за простия сценарий в оригиналния въпрос, когато актуализирате една стойност за всички редове в таблица с литерална стойност. Тази техника обаче работи много добре, ако актуализирате множество колони в таблица и източникът на вашата актуализация е друга заявка и искате да сведете до минимум записите и записите в регистрационните файлове на транзакциите. Освен това се представя по-добре от тестването на всяко поле с <>.
По-пълен пример може да бъде
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
)