Dlaczego to nie działa?
Uważam, że domyślnym zachowaniem SQL Server jest zwalnianie współdzielonych blokad, gdy tylko nie są już potrzebne. Twoje podzapytanie spowoduje krótkotrwałą współdzieloną blokadę (S) na stole, która zostanie zwolniona, gdy tylko podzapytanie się zakończy.
W tym momencie nic nie stoi na przeszkodzie, aby równoczesna transakcja wstawiła ten sam wiersz, który właśnie zweryfikowałeś, nie był obecny.
Jaką modyfikację muszę wprowadzić, aby nie było szansy na wyjątek z powodu naruszenia ograniczenia?
Dodanie HOLDLOCK
wskazówka do podzapytania poinstruuje SQL Server, aby trzymał blokadę do czasu zakończenia transakcji. (W twoim przypadku jest to transakcja niejawna.) HOLDLOCK
wskazówka jest odpowiednikiem SERIALIZABLE
wskazówka, która sama w sobie jest odpowiednikiem poziomu izolacji transakcji, który można serializować, o którym mowa na liście „innych podejść”.
HOLDLOCK
sama wskazówka wystarczyłaby, aby zachować blokadę S i uniemożliwić równoczesnej transakcji wstawienie wiersza, przed którym chronisz. Jednak prawdopodobnie zauważysz, że Twój unikalny błąd naruszenia klucza zostanie zastąpiony przez zakleszczenia, występujące z tą samą częstotliwością.
Jeśli zachowujesz tylko blokadę S na stole, rozważ wyścig między dwiema równoczesnymi próbami wstawienia tego samego wiersza, kontynuując w lockstep — obydwa udaje się uzyskać blokadę S na stole, ale żadnemu z nich nie uda się uzyskać wyłączności (X) zamek wymagany do wykonania wkładki.
Na szczęście istnieje inny typ blokady dla tego konkretnego scenariusza, zwany blokadą aktualizacji (U). Blokada U jest identyczna z blokadą S z następującą różnicą:podczas gdy wiele blokad S może być utrzymywanych jednocześnie na tym samym zasobie, tylko jedna blokada U może być jednocześnie utrzymywana. (Mówiąc inaczej, podczas gdy blokady S są ze sobą kompatybilne (tj. mogą współistnieć bez konfliktu), blokady U nie są ze sobą kompatybilne, ale mogą współistnieć z blokadami S; i dalej wzdłuż spektrum, blokady Exclusive (X) nie są kompatybilny z zamkami S lub U)
Możesz uaktualnić niejawną blokadę S w podzapytaniu do blokady U za pomocą UPDLOCK
wskazówka.
Dwie równoczesne próby wstawienia tego samego wiersza do tabeli zostaną teraz zserializowane w początkowej instrukcji select, ponieważ ta uzyskuje (i utrzymuje) blokadę U, która nie jest kompatybilna z inną blokadą U z równoczesnej próby wstawienia.
Wartości NULL
Osobny problem może wynikać z faktu, że FieldC dopuszcza wartości NULL.
Jeśli ANSI_NULLS
jest włączone (domyślnie), a następnie sprawdzanie równości FieldC=NULL
zwróci false, nawet w przypadku, gdy FieldC ma wartość NULL (musisz użyć IS NULL
operator do sprawdzania wartości null, gdy ANSI_NULLS
jest włączony). Ponieważ FieldC dopuszcza wartość NULL, sprawdzenie duplikatów nie zadziała podczas wstawiania wartości NULL.
Aby poprawnie radzić sobie z wartościami null, musisz zmodyfikować podzapytanie EXISTS tak, aby używało IS NULL
operator zamiast =
gdy wstawiana jest wartość NULL. (Lub możesz zmienić tabelę, aby nie zezwalać na wartości NULL we wszystkich odpowiednich kolumnach.)
Zasoby dotyczące SQL Server Books Online
- Wskazówki dotyczące blokowania
- Matryca zgodności blokady
- ANSI_NULLS