Podczas kompilowania i wykonywania kwerend SQL Server nie zajmuje czasu na ustalenie, czy instrukcja UPDATE faktycznie zmieni jakiekolwiek wartości, czy nie. Po prostu wykonuje zapisy zgodnie z oczekiwaniami, nawet jeśli jest to niepotrzebne.
W scenariuszu takim jak
update table1 set col1 = 'hello'
możesz pomyśleć, że SQL nic nie zrobi, ale zrobi – wykona wszystkie niezbędne zapisy tak, jakbyś faktycznie zmienił wartość. Dzieje się tak zarówno w przypadku tabeli fizycznej (lub indeksu klastrowego), jak i wszelkich indeksów nieklastrowanych zdefiniowanych w tej kolumnie. Powoduje to zapisy do fizycznych tabel/indeksów, przeliczanie indeksów i zapisy dziennika transakcji. Podczas pracy z dużymi zestawami danych istnieje ogromne korzyści w zakresie wydajności, jeśli chodzi o aktualizowanie tylko wierszy, które otrzymają zmianę.
Jeśli chcemy uniknąć narzutu związanego z tymi zapisami, gdy nie jest to konieczne, musimy znaleźć sposób, aby sprawdzić, czy nie ma potrzeby aktualizacji. Jednym ze sposobów sprawdzenia, czy aktualizacja jest konieczna, jest dodanie czegoś w rodzaju „gdzie kol <> 'cześć'.
update table1 set col1 = 'hello' where col1 <> 'hello'
Ale w niektórych przypadkach nie zadziałałoby to dobrze, na przykład jeśli aktualizujesz wiele kolumn w tabeli z wieloma wierszami i tylko niewielki podzbiór tych wierszy miałby faktycznie zmienione wartości. Wynika to z konieczności filtrowania według wszystkich tych kolumn, a predykaty nierówności generalnie nie są w stanie używać wyszukiwania indeksów, a także narzutu związanego z zapisami tabel i indeksów oraz wpisami w dzienniku transakcji, jak wspomniano powyżej.
Istnieje jednak znacznie lepsza alternatywa, wykorzystująca kombinację klauzuli EXISTS z klauzulą EXCEPT. Chodzi o to, aby porównać wartości w wierszu docelowym z wartościami w pasującym wierszu źródłowym, aby określić, czy aktualizacja jest rzeczywiście potrzebna. Spójrz na zmodyfikowane zapytanie poniżej i sprawdź dodatkowy filtr zapytań zaczynający się od EXISTS. Zwróć uwagę, że wewnątrz klauzuli EXISTS instrukcje SELECT nie zawierają klauzuli FROM. Ta część jest szczególnie ważna, ponieważ dodaje tylko dodatkowe stałe skanowanie i operację filtrowania w planie zapytania (koszt obu jest trywialny). Tak więc otrzymujesz bardzo lekką metodę określania, czy UPDATE jest w ogóle potrzebna, unikając niepotrzebnego narzutu na zapis.
update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists
(
/* DESTINATION */
select table1.col1
except
/* SOURCE */
select col1 = 'hello'
)
Wygląda to na zbyt skomplikowane w porównaniu ze sprawdzaniem aktualizacji w prostej klauzuli WHERE dla prostego scenariusza w pierwotnym pytaniu, gdy aktualizujesz jedną wartość dla wszystkich wierszy w tabeli z wartością literału. Jednak ta technika działa bardzo dobrze, jeśli aktualizujesz wiele kolumn w tabeli, a źródłem aktualizacji jest inne zapytanie i chcesz zminimalizować liczbę zapisów i wpisów w dziennikach transakcji. Działa również lepiej niż testowanie każdego pola za pomocą <>.
Bardziej kompletnym przykładem może być
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
)