Jestem oldskulowym użytkownikiem MySQL i zawsze wolałem JOIN
sub-zapytania. Ale obecnie wszyscy używają zapytań podrzędnych i nie znoszę tego; Nie wiem dlaczego.
Brakuje mi wiedzy teoretycznej, aby samodzielnie ocenić, czy jest jakaś różnica. Czy sub-zapytanie jest tak dobre jak JOIN
i dlatego nie ma się czym martwić?
Odpowiedzi:
Zaczerpnięte z podręcznika MySQL ( 13.2.10.11 Przepisywanie zapytań podrzędnych jako złączeń ):
Zatem podkwerendy mogą być wolniejsze niż
LEFT [OUTER] JOIN
, ale moim zdaniem ich siłą jest nieco większa czytelność.źródło
Join
isub query
ma inną składnię, więc czytelność nie może być porównywana, obie mają wyższą czytelność, o ile jesteś dobry w składni SQL. Wydajność jest ważniejsza.Zapytania podrzędne to logicznie poprawny sposób rozwiązywania problemów formularza „Uzyskaj fakty z A, zależnie od faktów z B”. W takich przypadkach logiczne jest umieszczanie B w zapytaniu cząstkowym niż łączenie. Jest to również bezpieczniejsze, w sensie praktycznym, ponieważ nie musisz być ostrożny w uzyskiwaniu zduplikowanych faktów z A ze względu na wielokrotne mecze przeciwko B.
W praktyce jednak odpowiedź zazwyczaj sprowadza się do wydajności. Niektórzy optymalizatorzy zasysają cytryny, gdy otrzymają sprzężenie w porównaniu z zapytaniem podrzędnym, a niektóre ssają cytryny w drugą stronę, i jest to specyficzne dla optymalizatora, specyficzne dla wersji DBMS i specyficzne dla zapytania.
Historycznie, jawne sprzężenia zwykle wygrywają, stąd ustalona mądrość, że sprzężenia są lepsze, ale optymalizatory są coraz lepsze, dlatego wolę pisać zapytania najpierw w logicznie spójny sposób, a następnie przeprowadzać restrukturyzację, jeśli uzasadniają to ograniczenia wydajności.
źródło
select custid from cust join bought using (custid) where price > 500
. Jeśli klient kupił wiele drogich przedmiotów, dostaniesz podwójne. Aby rozwiązać ten problem,select custid from cust where exists (select * from bought where custid = cust.custid and price > 500)
. Możesz użyćselect distinct …
zamiast tego, ale często jest to więcej pracy, zarówno dla optymalizatora, jak i oceniającego.W większości przypadków
JOIN
s są szybsze niż pod-zapytania i bardzo rzadko zdarza się, że pod-zapytanie jest szybsze.W
JOIN
RDBMS może stworzyć plan wykonania, który jest lepszy dla twojego zapytania i może przewidzieć, jakie dane powinny zostać załadowane do przetworzenia i zaoszczędzić czas, w przeciwieństwie do pod-zapytania, w którym będzie uruchamiać wszystkie zapytania i ładować wszystkie swoje dane do przetwarzania .Dobrą rzeczą w zapytaniach podrzędnych jest to, że są bardziej czytelne niż
JOIN
s: dlatego większość nowych osób SQL je woli; to prosty sposób; ale jeśli chodzi o wydajność, ŁĄCZENIA są lepsze w większości przypadków, nawet jeśli nie są trudne do odczytania.źródło
select * from a where a.x = (select b.x form b where b.id = a.id)
są bardzo małe w porównaniu do łączenia. Jest to bardzo specyficzny problem, ale w niektórych przypadkach prowadzi od godzin do minut.Użyj EXPLAIN, aby zobaczyć, w jaki sposób baza danych wykonuje zapytanie na twoich danych. W tej odpowiedzi jest ogromne „to zależy” ...
PostgreSQL może przepisać podzapytanie na złączenie lub dołączanie do podzapytania, jeśli uważa, że jedno jest szybsze od drugiego. Wszystko zależy od danych, indeksów, korelacji, ilości danych, zapytania itp.
źródło
W 2010 roku dołączyłbym do autora tych pytań i zdecydowanie głosowałbym
JOIN
, ale mając o wiele więcej doświadczenia (szczególnie w MySQL) mogę stwierdzić: Tak, podzapytania mogą być lepsze. Przeczytałem tutaj wiele odpowiedzi; niektóre podane podzapytania są szybsze, ale brakowało dobrego wyjaśnienia. Mam nadzieję, że mogę udzielić tej (bardzo) późnej odpowiedzi:Po pierwsze, pozwól mi powiedzieć najważniejsze: Istnieją różne formy podkwerend
I drugie ważne stwierdzenie: Rozmiar ma znaczenie
Jeśli korzystasz z zapytań podrzędnych, powinieneś zdawać sobie sprawę z tego, w jaki sposób serwer DB wykonuje zapytanie podrzędne. Zwłaszcza jeśli zapytanie cząstkowe jest oceniane raz lub dla każdego wiersza! Z drugiej strony nowoczesny serwer DB jest w stanie wiele zoptymalizować. W niektórych przypadkach podzapytanie pomaga zoptymalizować zapytanie, ale nowsza wersja serwera DB może sprawić, że optymalizacja stanie się przestarzała.
Zapytania podrzędne w Select-Fields
Należy pamiętać, że zapytanie podrzędne jest wykonywane dla każdego wynikowego wiersza z
foo
.Unikaj tego, jeśli to możliwe; może to drastycznie spowolnić zapytanie dotyczące ogromnych zestawów danych. Jeśli jednak zapytanie nie zawiera odniesienia
foo
, może być zoptymalizowane przez serwer DB jako zawartość statyczna i może być ocenione tylko raz.Zapytania częściowe w instrukcji Where
Jeśli masz szczęście, DB optymalizuje to wewnętrznie do
JOIN
. Jeśli nie, twoje zapytanie stanie się bardzo, bardzo wolne na ogromnych zestawach danych, ponieważ wykona pod-zapytanie dla każdego wiersza wfoo
, nie tylko wyników, jak w przypadku typu select.Zapytania częściowe w instrukcji Join
To jest interesujące. Łączymy
JOIN
z pod-zapytaniem. I tutaj otrzymujemy prawdziwą siłę podkwerend. Wyobraź sobie zestaw danych z milionami wierszy,wilco
ale tylko kilkoma odrębnymime
. Zamiast łączyć się z ogromnym stołem, mamy teraz mniejszy tymczasowy stół, do którego można dołączyć. Może to spowodować znacznie szybsze zapytania w zależności od wielkości bazy danych. Możesz mieć taki sam efekt za pomocąCREATE TEMPORARY TABLE ...
iINSERT INTO ... SELECT ...
, co może zapewnić lepszą czytelność bardzo złożonych zapytań (ale może zablokować zestawy danych na powtarzalnym poziomie izolacji odczytu).Zagnieżdżone zapytania cząstkowe
Można zagnieżdżać podzapytania na wielu poziomach. Może to pomóc w przypadku ogromnych zestawów danych, jeśli trzeba pogrupować lub posortować wyniki. Zazwyczaj serwer DB tworzy do tego celu tabelę tymczasową, ale czasami nie trzeba sortować według całej tabeli, a jedynie zestawu wyników. Może to zapewnić znacznie lepszą wydajność w zależności od wielkości tabeli.
Wniosek
Pod-zapytania nie zastępują a
JOIN
i nie należy ich używać w ten sposób (chociaż jest to możliwe). Moim skromnym zdaniem prawidłowe użycie zapytania częściowego to użycie go jako szybkiej zamianyCREATE TEMPORARY TABLE ...
. Dobre zapytanie podrzędne ogranicza zestaw danych w sposób, którego nie można osiągnąć wON
instrukcji aJOIN
. Jeśli zapytanie podrzędne zawiera jedno ze słów kluczowychGROUP BY
lubDISTINCT
i najlepiej nie znajduje się w polach wyboru lub instrukcji where, może to znacznie poprawić wydajność.źródło
Sub-queries in the Join-statement
: (1) wygenerowanie tabeli pochodnej z samego zapytania podrzędnego może zająć bardzo dużo czasu. (2) wynikowa tabela pochodna nie jest indeksowana. te dwa same mogą znacznie spowolnić SQL.10
rekordów, ponieważ nie ma indeksu, co nadal oznacza, że potencjalnie można zapytać 9 razy więcej rekordów danych niż bez tabeli tymczasowej podczas ŁĄCZENIA z innymi tabelami. BTW Miałem już ten problem z moim db (MySQL), w moim przypadku użycie pod-zapytaniaSELECT list
może być znacznie szybsze.EXPLAIN
z zapytania przed optymalizacją. Ze starymset profiling=1
można łatwo zobaczyć, czy tymczasowy stół jest wąskim gardłem. Nawet indeks wymaga czasu przetwarzania, B-Trees optymalizuje zapytania dotyczące rekordów, ale tablica 10 rekordów może być znacznie szybsza niż indeks dla milionów rekordów. Ale zależy to od wielu czynników, takich jak rozmiary i typy pól.Przede wszystkim, aby porównać dwa pierwsze, należy rozróżnić zapytania z podkwerendami do:
W przypadku pierwszej klasy zapytań dobry RDBMS zobaczy sprzężenia i podzapytania jako równoważne i wygeneruje te same plany zapytań.
W dzisiejszych czasach robi to nawet mysql.
Nadal czasami tak nie jest, ale nie oznacza to, że złączenia zawsze wygrywają - miałem przypadki, gdy korzystałem z podkwerend w mysql, poprawiając wydajność. (Na przykład, jeśli istnieje coś, co uniemożliwia planerowi mysql prawidłowe oszacowanie kosztu, a jeśli planista nie widzi wariantu łączenia i wariantu podzapytania jako tego samego, wówczas podzapytania mogą prześcignąć łączenia poprzez wymuszenie określonej ścieżki).
Wniosek jest taki, że powinieneś przetestować swoje zapytania dla wariantów łączenia i podzapytań, jeśli chcesz mieć pewność, które z nich będzie działać lepiej.
W przypadku drugiej klasy porównanie nie ma sensu, ponieważ tych zapytań nie można przepisać przy użyciu sprzężeń, aw takich przypadkach podkwerendy są naturalnym sposobem wykonywania wymaganych zadań i nie należy ich dyskryminować.
źródło
Myślę, że niedoceniana w cytowanych odpowiedziach jest kwestia duplikatów i problematycznych wyników, które mogą wynikać z konkretnych przypadków (użycia).
(chociaż Marcelo Cantos o tym wspomina)
Przytoczę przykład z kursów Lagunita Stanforda na temat SQL.
Tabela uczniów
Zastosuj tabelę
(wnioski złożone na określone uniwersytety i kierunki)
Spróbujmy znaleźć wyniki GPA dla studentów, którzy zgłosili się
CS
na studia wyższe (niezależnie od uczelni)Korzystanie z podzapytania:
Średnia wartość dla tego zestawu wyników wynosi:
Korzystanie z połączenia:
średnia wartość dla tego zestawu wyników:
Oczywiste jest, że druga próba daje mylące wyniki w naszym przypadku użycia, biorąc pod uwagę, że liczy się duplikaty do obliczenia wartości średniej. Oczywiste jest również, że użycie
distinct
instrukcji opartej na złączeniu nie wyeliminuje problemu, biorąc pod uwagę, że błędnie zatrzyma jedno z trzech wystąpień3.9
wyniku. Prawidłowym przypadkiem jest uwzględnienie DWÓCH (2) wystąpień3.9
wyniku, biorąc pod uwagę fakt, że faktycznie mamy DWÓCH (2) uczniów z wynikiem, który spełnia nasze kryteria zapytania.Wydaje się, że w niektórych przypadkach podpytanie jest najbezpieczniejszym sposobem, oprócz problemów z wydajnością.
źródło
Dokumentacja MSDN dla SQL Server mówi
więc jeśli potrzebujesz czegoś takiego
zamiast tego spróbuj użyć łączyć. W innych przypadkach nie ma znaczenia.
Mówię: tworzenie funkcji dla podkwerend eliminuje problem z zakłóceniami i pozwala na implementację dodatkowej logiki do podkwerend. Dlatego zalecam tworzenie funkcji dla podkwerend, gdy tylko jest to możliwe.
Zaśmiecenie kodu jest dużym problemem, a przemysł od dziesięcioleci pracuje nad jego unikaniem.
źródło
NOT EXISTS
. ANOT EXISTS
wygrywa PonadLEFT OUTER JOIN
z różnych powodów: PreFormance, nie-bezpieczeństwa (w przypadku nulable kolumn) i czytelności. sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-joinUruchom na bardzo dużej bazie danych ze starego Mambo CMS:
0 sekund
~ 3 sekundy
WYJAŚNIENIE pokazuje, że sprawdzają dokładnie taką samą liczbę wierszy, ale jedna zajmuje 3 sekundy, a jedna jest prawie natychmiastowa. Morał historii? Jeśli wydajność jest ważna (kiedy nie jest?), Wypróbuj ją na wiele sposobów i sprawdź, która z nich jest najszybsza.
I...
0 sekund
Ponownie te same wyniki, ta sama liczba zbadanych wierszy. Domyślam się, że DISTINCT mos_content.catid wymyśla znacznie dłużej niż DISTINCT mos_categories.id.
źródło
id
a nie coś takiego jakcatid
? Próbuję zoptymalizować mój dostęp do bazy danych, a twoje wnioski mogą pomóc.Zgodnie z moimi obserwacjami, tak jak w dwóch przypadkach, jeśli tabela ma mniej niż 100 000 rekordów, wówczas połączenie będzie działać szybko.
Ale w przypadku, gdy tabela ma ponad 100 000 rekordów, najlepszym wynikiem jest podzapytanie.
Mam jedną tabelę, która zawiera 500 000 rekordów, które utworzyłem poniżej zapytania, a czas jej zakończenia jest podobny
źródło
Podkwerendy są na ogół używane do zwrócenia pojedynczego wiersza jako wartości atomowej, chociaż można ich użyć do porównania wartości z wieloma wierszami ze słowem kluczowym IN. Są one dozwolone w prawie dowolnym znaczącym punkcie instrukcji SQL, w tym na liście docelowej, klauzuli WHERE i tak dalej. Proste kryterium zapytania może być wykorzystane jako warunek wyszukiwania. Na przykład między parą tabel:
Należy zauważyć, że użycie operatora wartości normalnej w wynikach zapytania podrzędnego wymaga zwrócenia tylko jednego pola. Jeśli chcesz sprawdzić, czy istnieje jedna wartość w zestawie innych wartości, użyj IN:
To oczywiście różni się od powiedzenia LEFT-JOIN, w którym po prostu chcesz dołączyć rzeczy z tabeli A i B, nawet jeśli warunek łączenia nie znajdzie żadnego pasującego rekordu w tabeli B itp.
Jeśli martwisz się o szybkość, musisz sprawdzić bazę danych i napisać dobre zapytanie i sprawdzić, czy jest jakaś znacząca różnica w wydajności.
źródło
Wersja MySQL: 5.5.28-0ubuntu0.12.04.2-log
Miałem też wrażenie, że JOIN jest zawsze lepszy niż pod-zapytanie w MySQL, ale EXPLAIN jest lepszym sposobem na osądzanie. Oto przykład, w którym zapytania cząstkowe działają lepiej niż JOIN.
Oto moje zapytanie z 3 pod-zapytaniami:
WYJAŚNIJ pokazuje:
To samo zapytanie z JOIN:
a wynikiem jest:
Porównanie
rows
kolumny wskazuje różnicę, a używane jest zapytanie z JOINUsing temporary; Using filesort
.Oczywiście, kiedy uruchamiam oba zapytania, pierwsze odbywa się za 0,02 sekundy, drugie nie kończy się nawet po 1 minucie, więc WYJAŚNIJ poprawnie te zapytania.
Jeśli nie mam INNER JOIN na
list_tag
stole, tj. Jeśli usunęz pierwszego zapytania i odpowiednio:
z drugiego zapytania funkcja EXPLAIN zwraca tę samą liczbę wierszy dla obu zapytań i oba te zapytania działają równie szybko.
źródło
Podkwerendy mają zdolność do obliczania funkcji agregacji w locie. Np. Znajdź minimalną cenę książki i uzyskaj wszystkie książki sprzedawane z tą ceną. 1) Korzystanie z podkwerend:
2) za pomocą JOIN
źródło
GROUP BY
s z różnymi tabelami: stackoverflow.com/questions/11415284/... Podkwerendy wydają się być bardziej ogólne. Zobacz także man MySQL: dev.mysql.com/doc/refman/5.7/en/optimizing-subqueries.html | dev.mysql.com/doc/refman/5.7/en/rewriting-subqueries.htmlNiektórzy twierdzą, że „niektóre RDBMS mogą przepisać podzapytanie na złączenie lub dołączanie do podzapytania, gdy sądzi, że jedno jest szybsze od drugiego”. Ale to stwierdzenie dotyczy prostych przypadków, na pewno nie w przypadku skomplikowanych zapytań z podzapytaniami, które w rzeczywistości powodują problemy z wydajnością.
źródło
Różnica jest widoczna tylko wtedy, gdy druga tabela łączenia zawiera znacznie więcej danych niż tabela podstawowa. Miałem doświadczenie jak poniżej ...
Mieliśmy tabelę użytkowników zawierającą sto tysięcy wpisów, a ich dane członkostwa (przyjaźń) około 300 tysięcy wpisów. Było to wspólne oświadczenie, aby zabrać przyjaciół i ich dane, ale z wielkim opóźnieniem. Ale działało dobrze, gdy w tabeli członkostwa znajdowała się tylko niewielka ilość danych. Gdy zmieniliśmy go, aby użyć pod-zapytania, działało dobrze.
Ale w międzyczasie zapytania dotyczące łączenia działają z innymi tabelami, które mają mniej wpisów niż tabela podstawowa.
Myślę więc, że instrukcje łączenia i kwerendy działają dobrze i zależy to od danych i sytuacji.
źródło
Obecnie wiele dbs może zoptymalizować podkwerendy i złączenia. Dlatego właśnie musisz zbadać swoje zapytanie za pomocą wyjaśnienia i zobaczyć, które z nich jest szybsze. Jeśli nie ma dużej różnicy w wydajności, wolę używać podkwerend, ponieważ są one proste i łatwiejsze do zrozumienia.
źródło
Właśnie myślę o tym samym problemie, ale używam podzapytania w części FROM. Potrzebuję połączenia i zapytania z dużych tabel, tabela „slave” ma 28 milionów rekordów, ale wynik to tylko 128, więc mały wynik big data! Używam na nim funkcji MAX ().
Najpierw używam LEFT JOIN, ponieważ myślę, że jest to właściwy sposób, mysql może zoptymalizować itp. Drugi raz tylko w celu przetestowania, przepisuję, aby podselekcjonować względem JOIN.
Środowisko wykonawcze LEFT JOIN: 1,12 s Środowisko uruchomieniowe SUB-SELECT: 0,06 s
18 razy szybszy wybór podrzędny niż łączenie! Tylko w chokito adv. Podselekcja wygląda okropnie, ale wynik ...
źródło
Jeśli chcesz przyspieszyć zapytanie za pomocą Join:
W przypadku „wewnętrznego łączenia / łączenia”, nie używaj warunku where zamiast tego używaj go w stanie „ON”. Na przykład:
W przypadku „Łączenia w lewo / w prawo”, nie używaj w stanie „WŁ.”, Ponieważ jeśli użyjesz łączenia w lewo / w prawo, otrzyma wszystkie wiersze dla dowolnej tabeli. Więc nie ma potrzeby używania go w „Włącz”. Więc spróbuj użyć warunku „Where”
źródło