Napotkaliśmy ciekawy problem z programem SQL Server. Rozważ następujący przykład repro:
CREATE TABLE #test (s_guid uniqueidentifier PRIMARY KEY);
INSERT INTO #test (s_guid) VALUES ('7E28EFF8-A80A-45E4-BFE0-C13989D69618');
SELECT s_guid FROM #test
WHERE s_guid = '7E28EFF8-A80A-45E4-BFE0-C13989D69618'
AND s_guid <> NEWID();
DROP TABLE #test;
Proszę na chwilę zapomnieć, że s_guid <> NEWID()
warunek wydaje się całkowicie bezużyteczny - to tylko minimalny przykład repro. Ponieważ prawdopodobieństwo NEWID()
dopasowania określonej stałej wartości jest bardzo małe, za każdym razem należy oceniać na PRAWDA.
Ale tak nie jest. Uruchomienie tego zapytania zwykle zwraca 1 wiersz, ale czasami (dość często, więcej niż 1 raz na 10) zwraca 0 wierszy. Reprodukowałem go z SQL Server 2008 w moim systemie i możesz go odtworzyć on-line ze skrzypkiem połączonym powyżej (SQL Server 2014).
Analiza planu wykonania ujawnia, że analizator zapytań najwyraźniej dzieli warunek na s_guid < NEWID() OR s_guid > NEWID()
:
... co całkowicie wyjaśnia, dlaczego czasami zawodzi (jeśli pierwszy wygenerowany identyfikator jest mniejszy, a drugi większy niż podany identyfikator).
Czy SQL Server może oceniać A <> B
jako A < B OR A > B
, nawet jeśli jedno z wyrażeń jest niedeterministyczne? Jeśli tak, gdzie to jest udokumentowane? Czy znaleźliśmy błąd?
Co ciekawe, AND NOT (s_guid = NEWID())
daje ten sam plan wykonania (i ten sam losowy wynik).
Znaleźliśmy ten problem, gdy programista chciał opcjonalnie wykluczyć określony wiersz i użył:
s_guid <> ISNULL(@someParameter, NEWID())
jako „skrót” dla:
(@someParameter IS NULL OR s_guid <> @someParameter)
Szukam dokumentacji i / lub potwierdzenia błędu. Kod nie jest tak istotny, więc obejścia nie są wymagane.
źródło
Odpowiedzi:
Jest to nieco kontrowersyjny punkt, a odpowiedź brzmi „tak”.
Najlepsza znana mi dyskusja została udzielona w odpowiedzi na raport o błędzie Connect Itzika Ben-Gana Błąd z NEWID i wyrażeniami tabelowymi , który został zamknięty, ponieważ nie zostanie naprawiony. Connect został wycofany, więc znajduje się tam link do archiwum internetowego. Niestety, wiele przydatnych materiałów zostało utraconych (lub utrudnionych do znalezienia) przez upadek Connect. W każdym razie najbardziej przydatne cytaty z Jim Hogg z Microsoft są:
Jednym z przykładów zmiany zachowania w tym zakresie w czasie jest to, że NULLIF działa niepoprawnie z niedeterministycznymi funkcjami, takimi jak RAND () . Istnieją również inne podobne przypadki, w których stosuje się np.
COALESCE
Podzapytanie, które może dawać nieoczekiwane wyniki i które są również rozwiązywane stopniowo.Jim kontynuuje:
Jest to konsekwencja normalizacji, która dzieje się bardzo wcześnie podczas kompilacji zapytań. Oba wyrażenia kompilują się dokładnie w tej samej znormalizowanej formie, więc tworzony jest ten sam plan wykonania.
źródło
Jest to udokumentowane (w pewnym sensie) tutaj:
Funkcje zdefiniowane przez użytkownika
Nie jest to jedyna forma zapytania, w której plan zapytań wykona wielokrotnie NEWID () i zmieni wynik. Jest to mylące, ale w rzeczywistości ma kluczowe znaczenie, aby NEWID () był przydatny do generowania kluczy i losowego sortowania.
Najbardziej mylące jest to, że nie wszystkie funkcje niedeterministyczne faktycznie zachowują się w ten sposób. Na przykład RAND () i GETDATE () będą wykonywane tylko raz na zapytanie.
źródło
=
,<
i>
może być skutecznie oceniany na podstawie BTree.Jeśli warto spojrzeć na ten stary standardowy dokument SQL 92 , wymagania dotyczące nierówności opisano w sekcji „
8.2 <comparison predicate>
” w następujący sposób:Uwaga: dla kompletności podałem 7b i 7h, ponieważ mówią one o
<>
porównaniu - nie sądzę, aby porównanie konstruktorów wartości wierszy z wieloma wartościami zostało zaimplementowane w T-SQL, chyba że po prostu bardzo nieporozumienie co to mówi - co jest całkiem możliweTo garść mylących śmieci. Ale jeśli chcesz kontynuować nurkowanie w śmietniku ...
Myślę , że 1.ii jest elementem stosowanym w tym scenariuszu, ponieważ porównujemy wartości „elementów konstruktora wartości wiersza”.
Zasadniczo powiedzenie
X <> Y
jest prawdziwe, jeśli wartości reprezentowane przez X i Y nie są równe. PonieważX < Y OR X > Y
jest to logicznie równoważne przepisanie tego orzeczenia, optymalizator może z niego skorzystać.Norma nie nakłada żadnych ograniczeń na tę definicję związanych z deterministyczną (lub cokolwiek, co otrzymujesz) elementami konstruktora wartości wiersza po obu stronach
<>
operatora porównania. Kod użytkownika odpowiada za to, że wyrażenie wartości po jednej stronie może być niedeterministyczne.źródło