Które z tych zapytań jest szybsze?
NIE ISTNIEJE:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE NOT EXISTS (
SELECT 1
FROM Northwind..[Order Details] od
WHERE p.ProductId = od.ProductId)
Lub NIE W:
SELECT ProductID, ProductName
FROM Northwind..Products p
WHERE p.ProductID NOT IN (
SELECT ProductID
FROM Northwind..[Order Details])
Plan wykonania zapytania mówi, że oba robią to samo. Jeśli tak jest, jaka jest zalecana forma?
Jest to oparte na bazie danych NorthWind.
[Edytować]
Właśnie znalazłem ten pomocny artykuł: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx
Myślę, że pozostanę przy NOT NOTIST.
sql
sql-server
notin
ilitirit
źródło
źródło
NOT IN
zapytanie:SELECT "A".* FROM "A" WHERE "A"."id" NOT IN (SELECT "B"."Aid" FROM "B" WHERE "B"."Uid" = 2)
jest prawie 30 razy szybsze niż toNOT EXISTS
:SELECT "A".* FROM "A" WHERE (NOT (EXISTS (SELECT 1 FROM "B" WHERE "B"."user_id" = 2 AND "B"."Aid" = "A"."id")))
Odpowiedzi:
Zawsze domyślnie
NOT EXISTS
.Plany wykonania mogą być w tej chwili takie same, ale jeśli którakolwiek kolumna zostanie zmieniona w przyszłości, aby umożliwić
NULL
s,NOT IN
wersja będzie musiała wykonać więcej pracy (nawet jeśliNULL
w danych faktycznie nie ma żadnych s) i semantykęNOT IN
jeśliNULL
s są obecne i tak prawdopodobnie nie będą tymi, których chcesz.Kiedy ani
Products.ProductID
czy[Order Details].ProductID
zezwolićNULL
SNOT IN
będą traktowane identycznie z poniższym zapytaniu.Dokładny plan może się różnić, ale dla moich przykładowych danych otrzymuję następujące informacje.
Wydaje się, że dość powszechnym nieporozumieniem jest to, że skorelowane zapytania są zawsze „złe” w porównaniu do złączeń. Z pewnością mogą się zdarzyć, gdy wymuszą plan zagnieżdżonych pętli (zapytanie podrzędne oceniane wiersz po rzędzie), ale ten plan zawiera operator logiczny anty semi-join. Sprzężenia anty semi nie są ograniczone do zagnieżdżonych pętli, ale mogą również używać sprzężenia mieszającego lub scalania (jak w tym przykładzie).
Jeśli
[Order Details].ProductID
jest możliwe,NULL
zapytanie staje sięPowodem tego jest to, że poprawna semantyka, jeśli
[Order Details]
zawiera jakieśNULL
ProductId
s, nie zwraca żadnych wyników. Zobacz dodatkową szpulę anty semi-join i szpulę zliczania wierszy, aby sprawdzić, czy została dodana do planu.Jeśli
Products.ProductID
zostanie również zmieniony, aby stałNULL
się dostępny, zapytanie zostanie zmienionePowodem tego jest to, że a
NULL
Products.ProductId
nie powinno być zwracane w wynikach, chyba żeNOT IN
zapytanie podrzędne nie zwróci w ogóle żadnych wyników (tj.[Order Details]
Tabela jest pusta). W takim przypadku powinien. W planie dla moich przykładowych danych jest to realizowane poprzez dodanie kolejnego anty-semi-join jak poniżej.Efekt tego jest pokazany w poście na blogu, do którego już linkował Buckley . W tym przykładzie liczba odczytów logicznych wzrasta z około 400 do 500 000.
Dodatkowo fakt, że jeden
NULL
może zmniejszyć liczbę wierszy do zera, bardzo utrudnia oszacowanie liczności. Jeśli SQL Server zakłada, że tak się stanie, ale w rzeczywistości nie maNULL
wierszy w danych, reszta planu wykonania może być katastrofalnie gorsza, jeśli jest to tylko część większego zapytania, z niewłaściwymi zagnieżdżonymi pętlami powodującymi powtarzające się wykonywanie drogiego podrzędnego drzewo na przykład .Nie jest to jedyny możliwy plan wykonania dla
NOT IN
naNULL
kolumnie -able jednak. W tym artykule pokazano kolejne zapytanie dotycząceAdventureWorks2008
bazy danych.Albowiem
NOT IN
naNOT NULL
kolumnie lubNOT EXISTS
na jednej z wartości pustych lub braku wartości pustych kolumnie daje następujący plan.Kiedy kolumna zmieni się na
NULL
-able,NOT IN
plan wygląda terazDodaje dodatkowy wewnętrzny operator łączenia do planu. To urządzenie jest wyjaśnione tutaj . Wszystko po to, aby przekonwertować poprzednie wyszukiwanie pojedynczego skorelowanego indeksu na
Sales.SalesOrderDetail.ProductID = <correlated_product_id>
dwa wyszukiwania na zewnętrzny wiersz. Dodatkowy jest włączonyWHERE Sales.SalesOrderDetail.ProductID IS NULL
.Ponieważ jest to pod łączeniem anty semi, jeśli ten zwróci jakiekolwiek wiersze, drugie wyszukiwanie nie nastąpi. Jeśli jednak
Sales.SalesOrderDetail
nie zawiera żadnychNULL
ProductID
, podwoi liczbę wymaganych operacji wyszukiwania.źródło
NOT EXISTS
funkcjonuje tak, jak się spodziewałemNOT IN
(a nie działa).Należy również pamiętać, że wartość NOT IN nie jest równoważna wartości NOT EXISTS, jeśli chodzi o wartość null.
Ten post wyjaśnia to bardzo dobrze
http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/
źródło
Jeśli planista wykonania powie, że są tacy sami, są tacy sami. Użyj tego, który sprawi, że twoja intencja stanie się bardziej oczywista - w tym przypadku drugiego.
źródło
Właściwie uważam, że byłby to najszybszy:
źródło
Mam tabelę, która ma około 120 000 rekordów i muszę wybrać tylko te, które nie istnieją (dopasowane z kolumną varchar) w czterech innych tabelach z liczbą wierszy około 1500, 4000, 40000, 200. Wszystkie zaangażowane tabele mają unikalny indeks w odpowiedniej
Varchar
kolumnie.NOT IN
zajęło około 10 minut,NOT EXISTS
zajęło 4 sekundy.Mam zapytanie rekurencyjne, które mogło mieć jakąś niezostrojoną sekcję, która mogła przyczynić się do 10 minut, ale inna opcja trwająca 4 sekundy wyjaśnia, przynajmniej dla mnie, że
NOT EXISTS
jest o wiele lepsza lub przynajmniej takaIN
iEXISTS
nie jest dokładnie taka sama i zawsze warta sprawdź przed użyciem kodu.źródło
W twoim przykładzie są one takie same, ponieważ optymalizator stwierdził, że to, co próbujesz zrobić, jest takie samo w obu przykładach. Ale możliwe jest, że w nietrywialnych przykładach optymalizator może tego nie zrobić, a w takim przypadku istnieją powody, aby czasami preferować jeden od drugiego.
NOT IN
powinno być preferowane, jeśli testujesz wiele wierszy w zewnętrznym zaznaczeniu. Podzapytanie wewnątrzNOT IN
instrukcji można ocenić na początku wykonywania, a tabelę tymczasową można porównać z każdą wartością w zewnętrznym zaznaczeniu, zamiast ponownie uruchamiać podselekcję za każdym razem, jak byłoby to wymagane w przypadkuNOT EXISTS
instrukcji.Jeśli podkwerenda musi być skorelowana z zewnętrznym wyborem,
NOT EXISTS
może być preferowane, ponieważ optymalizator może odkryć uproszczenie, które zapobiega tworzeniu jakichkolwiek tabel tymczasowych w celu wykonania tej samej funkcji.źródło
Używałem
i okazało się, że daje złe wyniki (Przez zło mam na myśli brak wyników). Ponieważ w TABLE2.Col1 była wartość NULL.
Podczas zmiany zapytania na
dało mi prawidłowe wyniki.
Od tego czasu zacząłem używać NOT EXISTS wszędzie.
źródło
Są bardzo podobne, ale tak naprawdę nie są takie same.
Pod względem wydajności stwierdziłem, że lewe sprzężenie jest bardziej efektywne w przypadku wyrażenia zerowego (kiedy należy wybrać dużą liczbę wierszy)
źródło
Jeśli optymalizator twierdzi, że są takie same, rozważ czynnik ludzki. Wolę zobaczyć NIE ISTNIEJE :)
źródło
Model tabeli bazy danych
Załóżmy, że mamy w naszej bazie danych następujące dwie tabele, które tworzą relację jeden do wielu.
student
Stół jest rodzicem, astudent_grade
to tabela dziecko, ponieważ ma to student_id kolumny klucz obcy odniesienia do identyfikatora kolumny klucza podstawowego w tabeli studentów.student table
Zawiera następujące dwa rekordy:, A
student_grade
tabela przechowuje klas otrzymali uczniowie:ISTNIEJE SQL
Powiedzmy, że chcemy, aby wszyscy uczniowie, którzy otrzymali 10 klas z matematyki.
Jeśli interesuje nas tylko identyfikator studenta, możemy uruchomić zapytanie takie jak to:
Ale aplikacja jest zainteresowana wyświetlaniem pełnej nazwy, a
student
nie tylko identyfikatora, dlatego potrzebujemy informacji zstudent
tabeli.Aby filtrować
student
rekordy, które mają 10 stopni z matematyki, możemy użyć operatora EXISTS SQL, takiego jak to:Po uruchomieniu powyższego zapytania widzimy, że wybrany jest tylko wiersz Alice:
Zewnętrzne zapytanie wybiera
student
kolumny wierszy, które chcemy powrócić do klienta. Jednak klauzula WHERE korzysta z operatora EXISTS z powiązanym wewnętrznym podzapytaniem.Operator EXISTS zwraca wartość true, jeśli podzapytanie zwraca co najmniej jeden rekord, a wartość false, jeśli nie wybrano żadnego wiersza. Aparat bazy danych nie musi całkowicie uruchamiać podkwerendy. Jeśli zostanie dopasowany pojedynczy rekord, operator EXISTS zwróci wartość true i wybrany zostanie powiązany inny wiersz zapytania.
Wewnętrzne podzapytanie jest skorelowane, ponieważ kolumna student_id w
student_grade
tabeli jest dopasowana do kolumny id zewnętrznej tabeli studenta.SQL NIE ISTNIEJE
Rozważmy, że chcemy wybrać wszystkich uczniów, którzy nie mają ocen niższych niż 9. W tym celu możemy użyć NOT EXISTS, co neguje logikę operatora EXISTS.
Dlatego operator NOT EXISTS zwraca true, jeśli bazowe podzapytanie nie zwraca rekordu. Jeśli jednak pojedynczy rekord zostanie dopasowany do wewnętrznego podzapytania, operator NOT EXISTS zwróci false, a wykonanie podzapytania można zatrzymać.
Aby dopasować wszystkie rekordy studentów, które nie mają skojarzonego student_grade z wartością mniejszą niż 9, możemy uruchomić następujące zapytanie SQL:
Po uruchomieniu powyższego zapytania widzimy, że dopasowany jest tylko rekord Alice:
Zaletą korzystania z operatorów SQL EXISTS i NOT EXISTS jest to, że wykonywanie wewnętrznego podzapytania można zatrzymać, dopóki znaleziony zostanie pasujący rekord.
źródło
To zależy..
nie byłoby stosunkowo powolne, nie ma zbyt wiele, aby ograniczyć rozmiar sprawdzania zapytania, aby sprawdzić, czy klucz jest włączony. W tym przypadku preferowane byłoby EXISTS.
Ale w zależności od optymalizatora DBMS nie może być inaczej.
Jako przykład, kiedy EXISTS jest lepszy
źródło
IN
iEXISTS
uzyskaj ten sam plan w SQL Server . W każdym razie pytanie dotyczyNOT IN
vs.NOT EXISTS