Jeśli mam UPDATE
instrukcję, która tak naprawdę nie zmienia żadnych danych (ponieważ dane są już w stanie zaktualizowanym). Czy dodanie WHERE
klauzuli w celu zapobieżenia aktualizacji ma jakąkolwiek korzyść w zakresie wydajności ?
Na przykład, czy istnieje jakakolwiek różnica w szybkości wykonywania między UPDATE 1 i UPDATE 2 w następujących przypadkach:
CREATE TABLE MyTable (ID int PRIMARY KEY, Value int);
INSERT INTO MyTable (ID, Value)
VALUES
(1, 1),
(2, 2),
(3, 3);
-- UPDATE 1
UPDATE MyTable
SET
Value = 2
WHERE
ID = 2
AND Value <> 2;
SELECT @@ROWCOUNT;
-- UPDATE 2
UPDATE MyTable
SET
Value = 2
WHERE
ID = 2;
SELECT @@ROWCOUNT;
DROP TABLE MyTable;
Pytam dlatego, że potrzebuję liczby wierszy, aby uwzględnić niezmieniony wiersz, więc wiem, czy zrobić wstawkę, jeśli identyfikator nie istnieje. Jako taki użyłem formularza UPDATE 2. Jeśli korzystanie z formularza UPDATE 1 przynosi korzyść w zakresie wydajności, czy jest możliwe uzyskanie potrzebnej liczby wierszy?
sql-server
query-performance
update
Martin Brown
źródło
źródło
Odpowiedzi:
Z pewnością może istnieć niewielka różnica w wydajności spowodowana aktualizacją 1 :
Jaką różnicę należy jednak zmierzyć w systemie za pomocą schematu, danych i obciążenia systemu. Istnieje kilka czynników, które wpływają na to, jak duży wpływ ma nieaktualizowana AKTUALIZACJA:
UPDATE TableName SET Field1 = Field1
, wyzwalacz aktualizacji zostanie uruchomiony i zasygnalizuje, że pole zostało zaktualizowane (jeśli sprawdzisz za pomocą funkcji UPDATE () lub COLUMNS_UPDATED ) i że pole zarówno w tabeli, jakINSERTED
i wDELETED
tabeli ma tę samą wartość.Ponadto następujący rozdział podsumowujący znajduje się w artykule Paula White'a, Wpływ aktualizacji niezwiązanych z aktualizacją (jak zauważył @spaghettidba w komentarzu do swojej odpowiedzi):
Proszę pamiętać (zwłaszcza jeśli nie klikniesz linku, aby zobaczyć pełny artykuł Paula), następujące dwa elementy:
Aktualizacje, które nie aktualizują się, nadal mają pewne aktywności w dzienniku, co wskazuje, że transakcja zaczyna się i kończy. Tyle tylko, że nie następuje modyfikacja danych (co nadal stanowi dobrą oszczędność).
Jak wspomniałem powyżej, musisz przetestować swój system. Skorzystaj z tych samych zapytań badawczych, z których korzysta Paul i sprawdź, czy uzyskasz te same wyniki. Widzę nieco inne wyniki w moim systemie niż te pokazane w artykule. Nadal nie ma brudnych stron do napisania, ale trochę więcej aktywności w logach.
Upraszczając, jeśli masz do czynienia tylko z jednym rzędem, możesz wykonać następujące czynności:
W przypadku wielu wierszy można uzyskać informacje potrzebne do podjęcia tej decyzji przy użyciu
OUTPUT
klauzuli. Przechwytując dokładnie, które wiersze zostały zaktualizowane, możesz zawęzić elementy, aby wyszukać różnicę między nie aktualizowaniem wierszy, które nie istnieją, a nie aktualizowaniem wierszy, które istnieją, ale nie wymagają aktualizacji.Podstawową implementację pokazuję w następującej odpowiedzi:
Jak uniknąć używania zapytania scalającego podczas wstawiania wielu danych za pomocą parametru xml?
Metoda pokazana w tej odpowiedzi nie odfiltrowuje istniejących wierszy, które nie wymagają aktualizacji. Tę część można dodać, ale najpierw musisz dokładnie pokazać, gdzie otrzymujesz zestaw danych, z którym się łączysz
MyTable
. Czy pochodzą z tymczasowego stołu? Parametr wyceniony w tabeli (TVP)?AKTUALIZACJA 1:
W końcu mogłem przeprowadzić testy i oto, co znalazłem w odniesieniu do dziennika transakcji i blokowania. Najpierw schemat tabeli:
Następnie test aktualizuje pole do wartości, która już ma:
Wyniki:
Na koniec test, który odfiltrowuje aktualizację z powodu niezmienionej wartości:
Wyniki:
Jak widać, podczas odfiltrowywania wiersza nic nie jest zapisywane w Dzienniku transakcji, w przeciwieństwie do dwóch pozycji oznaczających początek i koniec transakcji. I chociaż prawdą jest, że te dwa wpisy są prawie niczym, wciąż są czymś.
Również blokowanie zasobów PAGE i KEY jest mniej restrykcyjne podczas odfiltrowywania wierszy, które nie uległy zmianie. Jeśli żadne inne procesy nie wchodzą w interakcje z tą tabelą, to prawdopodobnie nie jest to problem (ale jak prawdopodobne jest to naprawdę?). Należy pamiętać, że testy pokazane na każdym z blogów, do których prowadzą linki (a nawet moje testy), domyślnie zakładają, że nie ma sprzeczności na stole, ponieważ nigdy nie jest to część testów. Mówiąc, że aktualizacje nie są tak lekkie, że filtrowanie się nie opłaca, należy przeprowadzić z odrobiną soli, ponieważ testy przeprowadzono mniej więcej w próżni. Ale w produkcji ta tabela najprawdopodobniej nie jest izolowana. Oczywiście równie dobrze może być tak, że odrobina rejestrowania i bardziej restrykcyjne blokady nie przekładają się na mniejszą wydajność. Więc najbardziej wiarygodne źródło informacji, aby odpowiedzieć na to pytanie? SQL Server. Konkretnie:twój SQL Server. Pokaże Ci, która metoda jest lepsza dla twojego systemu :-).
AKTUALIZACJA 2:
Jeśli operacje, w których nowa wartość jest taka sama jak bieżąca wartość (tj. Bez aktualizacji), liczą operacje, w których nowa wartość jest inna i aktualizacja jest konieczna, to następujący wzór może okazać się jeszcze lepszy, szczególnie jeśli na stole jest wiele sporów. Chodzi o to, aby
SELECT
najpierw zrobić prosty, aby uzyskać bieżącą wartość. Jeśli nie otrzymasz wartości, masz odpowiedź dotyczącąINSERT
. Jeśli masz wartość, możesz zrobić prosteIF
i wydaćUPDATE
tylko jeśli jest to potrzebne.Wyniki:
Tak więc uzyskano tylko 2 blokady zamiast 3, i obie te blokady są zamierzone współużytkowane, a nie Intent eXclusive lub Intent Update ( zgodność zamków ). Pamiętając, że każda nabyta blokada zostanie również zwolniona, każda blokada to tak naprawdę 2 operacje, więc ta nowa metoda to w sumie 4 operacje zamiast 6 operacji w pierwotnie zaproponowanej metodzie. Biorąc pod uwagę, że ta operacja jest wykonywana raz na 15 ms (w przybliżeniu, jak podano w OP), czyli około 66 razy na sekundę. Oryginalna propozycja wynosi więc 396 operacji blokowania / odblokowywania na sekundę, podczas gdy ta nowa metoda zapewnia jedynie 264 operacji blokowania / odblokowywania na sekundę nawet lżejszych blokad. Nie jest to gwarancja niesamowitej wydajności, ale z pewnością warte przetestowania :-).
źródło
Pomniejsz trochę i pomyśl o większym obrazie. Czy w rzeczywistości rzeczywiste oświadczenie o aktualizacji będzie wyglądać tak:
Czy może będzie to wyglądać bardziej tak:
Ponieważ w prawdziwym świecie tabele mają wiele kolumn. Oznacza to, że będziesz musiał wygenerować dużo złożonej dynamicznej logiki aplikacji, aby zbudować ciągi dynamiczne, LUB będziesz musiał za każdym razem określać zawartość każdego pola przed i po.
Jeśli budujesz te instrukcje aktualizacji dynamicznie dla każdej tabeli, tylko przekazując pola, które są aktualizowane, możesz szybko natknąć się na problem zanieczyszczenia pamięci podręcznej planu podobny do problemu wielkości parametrów NHibernate sprzed kilku lat. Co gorsza, jeśli zbudujesz instrukcje aktualizacji w SQL Server (tak jak w procedurach przechowywanych), to spalisz cenne cykle procesora, ponieważ SQL Server nie jest strasznie wydajny w łączeniu łańcuchów razem na dużą skalę.
Ze względu na tę złożoność zwykle nie ma sensu przeprowadzać tego rodzaju porównania rząd po rzędzie podczas aktualizacji. Zamiast tego pomyśl o operacjach opartych na zbiorze.
źródło
Można zauważyć wzrost wydajności podczas pomijania wierszy, które nie muszą być aktualizowane tylko wtedy, gdy liczba wierszy jest duża (mniej rejestrowania, mniej brudnych stron do zapisu na dysku).
W przypadku aktualizacji w jednym wierszu, jak w twoim przypadku, różnica w wydajności jest całkowicie znikoma. Jeśli aktualizacja wierszy we wszystkich przypadkach ułatwia ci to, zrób to.
Aby uzyskać więcej informacji na ten temat, zobacz Nieprzeprowadzanie aktualizacji przez Paula White'a
źródło
Możesz połączyć aktualizację i wstawić w jedną instrukcję. W SQL Server można użyć instrukcji MERGE , aby wykonać aktualizację i wstawić, jeśli nie zostanie znaleziona. W przypadku MySQL można użyć INSERT ON DUPLICATE KEY UPDATE .
źródło
Zamiast sprawdzać wartości wszystkich pól, czy nie możesz uzyskać wartości skrótu za pomocą interesujących kolumn, a następnie porównać ją z wartością skrótu zapisaną względem wiersza w tabeli?
źródło