Załóżmy strukturę tabeli MyTable(KEY, datafield1, datafield2...)
.
Często chcę zaktualizować istniejący rekord lub wstawić nowy, jeśli nie istnieje.
Głównie:
IF (key exists)
run update command
ELSE
run insert command
Jak najlepiej to napisać?
Odpowiedzi:
nie zapomnij o transakcjach. Wydajność jest dobra, ale proste (JEŚLI ISTNIEJE) podejście jest bardzo niebezpieczne.
Gdy wiele wątków będzie próbowało wykonać wstawianie lub aktualizację, możesz łatwo uzyskać naruszenie klucza podstawowego.
Rozwiązania dostarczone przez @Beau Crawford i @Esteban pokazują ogólny pomysł, ale są podatne na błędy.
Aby uniknąć zakleszczeń i naruszeń PK, możesz użyć czegoś takiego:
lub
źródło
Zobacz moją szczegółową odpowiedź na bardzo podobne poprzednie pytanie
@Beau Crawford's jest dobrym sposobem w SQL 2005 i niższych, ale jeśli udzielasz rep, powinien przejść do pierwszego faceta, który go SO . Jedynym problemem jest to, że dla wstawek są to nadal dwie operacje IO.
MS Sql2008 wprowadza
merge
ze standardu SQL: 2003:Teraz to naprawdę tylko jedna operacja IO, ale okropny kod :-(
źródło
upsert
że wszyscy inni dostawcy DB zdecydowali się na wsparcie.upsert
Składnia jest o wiele ładniejszy sposób, aby to zrobić, więc w najgorszym MS powinien wspierać go zbyt - to nie jest tak, że to tylko niestandardowe kluczowe w T-SQLMERGE
składni.HOLDLOCK
operacji scalania w sytuacjach o wysokiej współbieżności.Wykonaj UPSERT:
http://en.wikipedia.org/wiki/Upsert
źródło
Wiele osób zasugeruje ci użycie
MERGE
, ale ostrzegam cię przed tym. Domyślnie nie chroni cię przed współbieżnością i warunkami wyścigowymi więcej niż wiele oświadczeń, ale wprowadza inne niebezpieczeństwa:http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Nawet przy tej dostępnej „prostszej” składni nadal wolę to podejście (pominięcie obsługi błędów ze względu na zwięzłość):
Wielu ludzi zasugeruje w ten sposób:
Ale to wszystko zapewnia, że konieczne może być dwukrotne przeczytanie tabeli w celu zlokalizowania wierszy, które mają zostać zaktualizowane. W pierwszej próbce będziesz musiał tylko zlokalizować rząd (wiersze) tylko raz. (W obu przypadkach, jeśli nie zostaną znalezione wiersze z początkowego odczytu, nastąpi wstawienie).
Inni sugerują w ten sposób:
Jest to jednak problematyczne, jeśli tylko z innego powodu niż zezwolenie SQL Serverowi na wychwytywanie wyjątków, którym można było zapobiec, jest znacznie droższe, z wyjątkiem rzadkiego scenariusza, w którym prawie każda wstawka ulega awarii. Udowadniam tutaj tyle samo:
źródło
UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Edytować:
Niestety, nawet na własną szkodę, muszę przyznać, że rozwiązania, które dokonują tego bez wyboru, wydają się lepsze, ponieważ wykonują zadanie o jeden krok mniej.
źródło
Jeśli chcesz przesłać więcej niż jeden rekord na raz, możesz użyć instrukcji ANSI SQL: 2003 DML MERGE.
Sprawdź naśladowanie instrukcji MERGE w SQL Server 2005 .
źródło
Chociaż jest dość późno, aby to skomentować, chcę dodać bardziej kompletny przykład za pomocą MERGE.
Takie instrukcje Insert + Update są zwykle nazywane instrukcjami „Upsert” i można je zaimplementować za pomocą funkcji MERGE w programie SQL Server.
Bardzo dobry przykład podano tutaj: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
Powyżej wyjaśnia również scenariusze blokowania i współbieżności.
Będę cytować to samo w celach informacyjnych:
źródło
Zamień nazwy tabel i pól na dowolne potrzebne. Dbać o stosowania na stanie. Następnie ustaw odpowiednią wartość (i typ) dla zmiennych w wierszu DECLARE.
Twoje zdrowie.
źródło
Można użyć
MERGE
instrukcji, instrukcja służy do wstawiania danych, jeśli nie istnieje, lub aktualizacji, jeśli istnieje.źródło
W przypadku aktualizacji UPDATE, jeśli nie zaktualizowano wierszy, a następnie INSERT, należy najpierw rozważyć wprowadzenie instrukcji INSERT, aby zapobiec warunkom wyścigu (zakładając, że nie ma potrzeby usuwania USUŃ)
Oprócz uniknięcia wyścigu, jeśli w większości przypadków rekord już istnieje, spowoduje to awarię INSERT, marnując procesor.
Używanie MERGE prawdopodobnie jest lepsze dla SQL2008 i późniejszych.
źródło
To zależy od wzorca użytkowania. Trzeba patrzeć na duży obraz użytkowania bez zagubienia się w szczegółach. Na przykład, jeśli wzorzec użycia to 99% aktualizacji po utworzeniu rekordu, najlepszym rozwiązaniem jest „UPSERT”.
Po pierwszym wstawieniu (trafieniu) będą to wszystkie aktualizacje pojedynczej instrukcji, bez ifs lub ale. Warunek „gdzie” na wkładce jest konieczny, w przeciwnym razie wstawi duplikaty i nie chcesz zajmować się blokowaniem.
źródło
MS SQL Server 2008 wprowadza instrukcję MERGE, która moim zdaniem jest częścią standardu SQL: 2003. Jak wielu pokazało, nie jest wielką sprawą zajmowanie się przypadkami z jednym wierszem, ale w przypadku dużych zestawów danych potrzebny jest kursor wraz ze wszystkimi problemami z wydajnością. Instrukcja MERGE będzie mile widzianym dodatkiem w przypadku dużych zestawów danych.
źródło
Zanim wszyscy przejdą do HOLDLOCK-ów ze strachu przed tymi okropnymi użytkownikami, którzy bezpośrednio uruchamiają twoje sproki :-) pozwól mi zaznaczyć, że musisz zagwarantować unikalność nowych PK-ów według projektu (klucze tożsamości, generatory sekwencji w Oracle, unikalne indeksy dla zewnętrzne identyfikatory, zapytania objęte indeksami). To jest alfa i omega problemu. Jeśli tego nie masz, żadne HOLDLOCK-y wszechświata cię nie uratują, a jeśli tak, to nie potrzebujesz niczego poza UPDLOCK przy pierwszym wyborze (lub najpierw użyj aktualizacji).
Sprocs zwykle działają w ściśle kontrolowanych warunkach i przy założeniu, że osoba dzwoniąca jest zaufana (poziom środkowy). Oznacza to, że jeśli prosty wzorzec wstawiania (aktualizacja + wstawianie lub scalanie) kiedykolwiek zobaczy duplikat PK, oznacza to błąd w projekcie warstwy pośredniej lub tabeli i dobrze, że SQL wykrzyknie błąd w takim przypadku i odrzuci rekord. Umieszczenie HOLDLOCK w tym przypadku jest równoznaczne z jedzeniem wyjątków i przyjmowaniem potencjalnie błędnych danych, oprócz zmniejszenia wydajności.
Powiedziawszy to, używając MERGE lub UPDATE, INSERT jest łatwiejszy na twoim serwerze i mniej podatny na błędy, ponieważ nie musisz pamiętać, aby dodać (UPDLOCK), aby najpierw wybrać. Ponadto, jeśli robisz wstawki / aktualizacje w małych partiach, musisz znać swoje dane, aby zdecydować, czy transakcja jest odpowiednia, czy nie. Jest to tylko zbiór niepowiązanych zapisów, a wówczas dodatkowa transakcja „otaczania” będzie szkodliwa.
źródło
Czy warunki wyścigu naprawdę mają znaczenie, jeśli najpierw spróbujesz aktualizacji, a następnie wstawki? Powiedzmy, że masz dwa wątki, które chcą ustawić wartość dla klucza klucza :
Wątek 1: wartość = 1
Wątek 2: wartość = 2
Przykładowy scenariusz wyścigu
Drugi wątek kończy się niepowodzeniem przy wstawianiu (z błędnym duplikatem klucza) - wątek 2.
Ale; w środowisku wielowątkowym harmonogram systemu operacyjnego decyduje o kolejności wykonania wątku - w powyższym scenariuszu, w którym mamy ten warunek wyścigu, to system operacyjny zdecydował o kolejności wykonania. Tzn .: Błędem jest twierdzenie, że „wątek 1” lub „wątek 2” był „pierwszy” z systemowego punktu widzenia.
Kiedy czas wykonania jest tak blisko dla wątku 1 i wątku 2, wynik warunków wyścigu nie ma znaczenia. Jedynym wymaganiem powinno być, aby jeden z wątków zdefiniował wynikową wartość.
Do wdrożenia: jeśli aktualizacja, po której następuje wstawienie, powoduje błąd „duplikowanie klucza”, należy to traktować jako sukces.
Oczywiście nie należy oczywiście zakładać, że wartość w bazie danych jest taka sama, jak wartość, którą zapisałeś jako ostatni.
źródło
W SQL Server 2008 można użyć instrukcji MERGE
źródło
Wypróbowałem poniższe rozwiązanie i działa ono dla mnie, gdy pojawia się współbieżne żądanie instrukcji insert.
źródło
Możesz użyć tego zapytania. Praca we wszystkich wersjach SQL Server. To proste i jasne. Ale potrzebujesz użyć 2 zapytań. Możesz użyć, jeśli nie możesz użyć MERGE
UWAGA: Proszę wyjaśnić odpowiedzi przeczące
źródło
Jeśli używasz ADO.NET, DataAdapter obsługuje to.
Jeśli chcesz sobie z tym poradzić, jest to następujący sposób:
Upewnij się, że kolumna klucza zawiera ograniczenie klucza podstawowego.
Więc ty:
Możesz to również zrobić w drugą stronę, tj. Najpierw wstawić i wykonać aktualizację, jeśli wstawienie się nie powiedzie. Zwykle pierwszy sposób jest lepszy, ponieważ aktualizacje są wykonywane częściej niż wstawki.
źródło
Wykonanie if istnieje ... w przeciwnym razie ... wymaga wykonania co najmniej dwóch żądań (jednego do sprawdzenia, drugiego do podjęcia działania). Poniższe podejście wymaga tylko jednego, w którym istnieje rekord, dwóch, jeśli wymagana jest wstawka:
źródło
Zazwyczaj robię to, co powiedziało kilka innych plakatów, odnosząc się najpierw do sprawdzenia, czy istnieje, a potem robię wszystko, co jest właściwe. Jedną z rzeczy, o których należy pamiętać, jest to, że plan wykonania buforowany przez sql może być nieoptymalny dla jednej lub drugiej ścieżki. Uważam, że najlepszym sposobem na to jest wywołanie dwóch różnych procedur składowanych.
Teraz nie stosuję się często do moich rad, więc weź to z odrobiną soli.
źródło
Wybierz, jeśli otrzymasz wynik, zaktualizuj go, jeśli nie, utwórz go.
źródło