Join Vs. sub-query

Jestem oldskulowym użytkownikiem MySQL i zawsze preferowałem JOIN zamiast pod-zapytań. Ale w dzisiejszych czasach każdy używa sub-query, i nienawidzę go; Nie wiem dlaczego.

Brak mi wiedzy teoretycznej, by samemu ocenić, czy jest jakaś różnica. Czy pod-zapytanie jest tak dobre jak JOIN i dlatego nie ma się czym martwić?
Author: Peter Mortensen, 2010-04-05

19 answers

Zaczerpnięte z instrukcji MySQL (13.2.10.11 przepisywanie zapytań podrzędnych jako Joins):

LEFT [OUTER] JOIN może być szybszy niż równoważne zapytanie podrzędne, ponieważ serwer może być w stanie zoptymalizować go lepiej-fakt, który nie jest specyficzny dla samego serwera MySQL.

Więc zapytania podrzędne mogą być wolniejsze niż LEFT [OUTER] JOIN, ale moim zdaniem ich siłą jest nieco wyższa czytelność.

 216
Author: simhumileco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-25 15:52:52

Sub-kwerendy są logicznie poprawnym sposobem rozwiązywania problemów formularza "Get facts from A, conditional on facts from B". W takich przypadkach bardziej logiczne jest trzymanie B w zapytaniu podrzędnym niż łączenie. Jest to również bezpieczniejsze, w sensie praktycznym, ponieważ nie musisz być ostrożny w uzyskiwaniu zduplikowanych faktów z A z powodu wielu meczów przeciwko B.

Praktycznie rzecz biorąc, odpowiedź zwykle sprowadza się do wydajności. Niektórzy optymalizatorzy ssą cytryny, gdy podano join vs sub-query, a niektóre ssać cytryny w drugą stronę, i to jest optimiser-specific, DBMS-version-specific i query-specific.

Historycznie, explicit joins zwykle wygrywa, stąd ustalona mądrość, która łączy jest lepsza, ale optymalizatory są coraz lepsze cały czas, więc wolę pisać zapytania najpierw w logicznie spójny sposób, a następnie zrestrukturyzować, jeśli wymagają tego ograniczenia wydajności.

 896
Author: Marcelo Cantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-05 06:26:49

W większości przypadków JOINS są szybsze niż zapytania podrzędne i bardzo rzadko zdarza się, aby zapytania podrzędne były szybsze.

W JOIN S RDBMS może stworzyć plan wykonania, który jest lepszy dla Twojego zapytania i może przewidzieć, jakie dane mają być załadowane do przetworzenia i zaoszczędzić czas, w przeciwieństwie do zapytania podrzędnego, w którym uruchomi wszystkie zapytania i załaduje wszystkie swoje dane do przetworzenia.

Dobrą rzeczą w zapytaniach podrzędnych jest to, że są one bardziej czytelne niż JOINs: dlatego większość nowych ludzi SQL preferuje je; to jest łatwy sposób, ale jeśli chodzi o wydajność, połączenia są lepsze w większości przypadków, mimo że nie są trudne do odczytania zbyt.

 362
Author: Kronass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-15 20:38:50

Użyj EXPLAIN, aby zobaczyć, jak twoja baza danych wykonuje zapytanie na Twoich danych. W tej odpowiedzi jest ogromne "to zależy"...

PostgreSQL może przepisać subquery do join lub join do subquery, gdy myśli, że jedno jest szybsze od drugiego. Wszystko zależy od danych, indeksów, korelacji, ilości danych, zapytań itp.

 131
Author: Frank Heikens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-05 07:37:42

W roku 2010 dołączyłbym do autora tego pytania i zdecydowanie zagłosowałbym na JOIN, ale z większym doświadczeniem (zwłaszcza w MySQL) mogę stwierdzić: tak subqueries mogą być lepsze. Czytałem wiele odpowiedzi tutaj; Niektóre podane zapytania podrzędne są szybsze, ale brakowało dobrego wyjaśnienia. Mam nadzieję, że uda mi się udzielić takiej (bardzo) późnej odpowiedzi: {]}

Po pierwsze, powiem najważniejsze: istnieją różne formy zapytań podrzędnych

I drugie ważne stwierdzenie: rozmiar ma znaczenie

Jeśli używasz sub-zapytań, powinieneś być świadomy Jak DB-Server wykonuje sub-zapytanie. Szczególnie , jeśli zapytanie podrzędne jest oceniane raz lub dla każdego wiersza! Z drugiej strony, nowoczesny serwer DB jest w stanie wiele zoptymalizować. W niektórych przypadkach zapytanie podrzędne pomaga w optymalizacji zapytania, ale nowsza wersja DB-Server może sprawić, że optymalizacja stanie się przestarzała.

Zapytania podrzędne w Select-Fields

SELECT moo, (SELECT roger FROM wilco WHERE moo = me) AS bar FROM foo

Be świadoma, że dla każdego wiersza wynikowego z foo wykonywane jest zapytanie podrzędne.
Unikaj tego, jeśli to możliwe; może to drastycznie spowolnić Twoje zapytanie na ogromnych zestawach danych. Jednakże, jeśli zapytanie podrzędne nie ma odniesienia do foo, może być zoptymalizowane przez DB-server jako zawartość statyczna i może być ocenione tylko raz.

Zapytania podrzędne w instrukcji Where

SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)

Jeśli masz szczęście, DB optymalizuje to wewnętrznie do JOIN. Jeśli nie, twoje zapytanie stanie się bardzo, bardzo powolne na ogromnych zestawach danych ponieważ wykona pod-zapytanie dla każdego wiersza w foo, a nie tylko wyniki jak w select-type.

Zapytania podrzędne w deklaracji Join

SELECT moo, bar 
  FROM foo 
    LEFT JOIN (
      SELECT MIN(bar), me FROM wilco GROUP BY me
    ) ON moo = me
To ciekawe. Łączymy JOIN z podzapytaniem. I tutaj mamy prawdziwą siłę sub-zapytań. Wyobraź sobie zbiór danych z milionami wierszy w wilco, ale tylko kilkoma odrębnymi me. Zamiast łączyć się przeciwko ogromnemu stołowi, mamy teraz mniejszy tymczasowy stół, aby dołączyć przeciwko. Może to skutkować znacznie szybszymi zapytaniami w zależności od wielkości bazy danych. Taki sam efekt można uzyskać z CREATE TEMPORARY TABLE ... i INSERT INTO ... SELECT ..., co może zapewnić lepszą czytelność przy bardzo złożonych zapytaniach (ale może zablokować zbiory danych na powtarzalnym poziomie izolacji odczytu).

Zagnieżdżone zapytania podrzędne

SELECT moo, bar
  FROM (
    SELECT moo, CONCAT(roger, wilco) AS bar
      FROM foo
      GROUP BY moo
      HAVING bar LIKE 'SpaceQ%'
  ) AS temp_foo
  ORDER BY bar

Można zagnieżdżać zapytania podrzędne na wielu poziomach. Może to pomóc w przypadku dużych zbiorów danych, jeśli musisz grupować lub sortować wyniki. Zazwyczaj DB-Server tworzy do tego tabelę tymczasową, ale czasami nie trzeba sortować całej tabeli, tylko na resultset. Może to zapewnić znacznie lepszą wydajność w zależności od wielkości tabeli.

Podsumowanie

Zapytania podrzędne nie zastępują JOIN i nie powinieneś ich używać w ten sposób (chociaż jest to możliwe). Moim skromnym zdaniem, poprawne użycie sub-kwerendy jest użycie jako szybkiego zastąpienia CREATE TEMPORARY TABLE .... Dobre zapytanie podrzędne zmniejsza zbiór danych w sposób, którego nie można wykonać w instrukcji ON JOIN. Jeśli zapytanie podrzędne zawiera jedno ze słów kluczowych GROUP BY lub DISTINCT i jest najlepiej nie znajdować się w polach select lub instrukcji where, wtedy może to znacznie poprawić wydajność.

 99
Author: Trendfischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-22 16:31:16

Po pierwsze, aby porównać te dwa pierwsze należy odróżnić zapytania z zapytaniami podrzędnymi do:

  1. Klasa zapytań podrzędnych, które zawsze mają odpowiednie równoważne zapytanie napisane za pomocą łączników
  2. Klasa zapytań podrzędnych, których nie można przepisać za pomocą joinsów

Dla pierwszej klasy zapytań dobry RDBMS będzie widział połączenia i zapytania podrzędne jako równoważne i wytworzy te same plany zapytań.

W dzisiejszych czasach nawet mysql tak robi.

Still, czasami tak nie jest, ale to nie znaczy, że jointy zawsze wygrywają - miałem przypadki, gdy korzystałem z podqueries w mysql poprawiłem wydajność. (Na przykład, jeśli coś uniemożliwia mysql planner poprawne oszacowanie kosztów i jeśli Planer nie widzi join-variant i subquery-variant jako takie same, to zapytania podrzędne mogą przewyższyć połączenia wymuszając określoną ścieżkę).

Wniosek jest taki, że powinieneś przetestować swoje zapytania zarówno dla wariantów join, jak i subquery, jeśli chcesz mieć pewność który z nich będzie działał lepiej.

Dla drugiej klasy porównanie nie ma sensu, ponieważ zapytania te nie mogą być przepisywane za pomocą joinsów i w takich przypadkach zapytania podrzędne są naturalnym sposobem wykonywania wymaganych zadań i nie należy ich dyskryminować.

 43
Author: Unreason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-22 07:57:32

Myślę, że to, co zostało podkreślone w cytowanych odpowiedziach, to kwestia duplikatów i problematycznych wyników, które mogą wynikać z konkretnych (użytkowych) przypadków.

(choć marcelo Cantos o tym wspomina)

Przytoczę przykład z kursów Lagunita Stanforda na temat SQL.

Tabela Uczniów

+------+--------+------+--------+
| sID  | sName  | GPA  | sizeHS |
+------+--------+------+--------+
|  123 | Amy    |  3.9 |   1000 |
|  234 | Bob    |  3.6 |   1500 |
|  345 | Craig  |  3.5 |    500 |
|  456 | Doris  |  3.9 |   1000 |
|  567 | Edward |  2.9 |   2000 |
|  678 | Fay    |  3.8 |    200 |
|  789 | Gary   |  3.4 |    800 |
|  987 | Helen  |  3.7 |    800 |
|  876 | Irene  |  3.9 |    400 |
|  765 | Jay    |  2.9 |   1500 |
|  654 | Amy    |  3.9 |   1000 |
|  543 | Craig  |  3.4 |   2000 |
+------+--------+------+--------+

Zastosuj Tabelę

[10]} (wnioski składane na konkretne uczelnie i kierunki)
+------+----------+----------------+----------+
| sID  | cName    | major          | decision |
+------+----------+----------------+----------+
|  123 | Stanford | CS             | Y        |
|  123 | Stanford | EE             | N        |
|  123 | Berkeley | CS             | Y        |
|  123 | Cornell  | EE             | Y        |
|  234 | Berkeley | biology        | N        |
|  345 | MIT      | bioengineering | Y        |
|  345 | Cornell  | bioengineering | N        |
|  345 | Cornell  | CS             | Y        |
|  345 | Cornell  | EE             | N        |
|  678 | Stanford | history        | Y        |
|  987 | Stanford | CS             | Y        |
|  987 | Berkeley | CS             | Y        |
|  876 | Stanford | CS             | N        |
|  876 | MIT      | biology        | Y        |
|  876 | MIT      | marine biology | N        |
|  765 | Stanford | history        | Y        |
|  765 | Cornell  | history        | N        |
|  765 | Cornell  | psychology     | Y        |
|  543 | MIT      | CS             | N        |
+------+----------+----------------+----------+

Spróbujmy znaleźć wyniki GPA dla studenci, którzy zgłosili się na kierunek CS (niezależnie od uczelni)

Korzystanie z zapytania podrzędnego:

select GPA from Student where sID in (select sID from Apply where major = 'CS');

+------+
| GPA  |
+------+
|  3.9 |
|  3.5 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

Średnia wartość dla tego zestawu wyników wynosi:

select avg(GPA) from Student where sID in (select sID from Apply where major = 'CS');

+--------------------+
| avg(GPA)           |
+--------------------+
| 3.6800000000000006 |
+--------------------+

Korzystanie z łącznika:

select GPA from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+------+
| GPA  |
+------+
|  3.9 |
|  3.9 |
|  3.5 |
|  3.7 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

Średnia wartość dla tego zestawu wyników:

select avg(GPA) from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+-------------------+
| avg(GPA)          |
+-------------------+
| 3.714285714285714 |
+-------------------+

Jest oczywiste, że druga próba daje mylące wyniki w naszym przypadku użycia, biorąc pod uwagę, że liczy duplikaty dla obliczenia średniej wartości. Jest to również oczywiste to użycie {[7] } z wyrażeniem opartym na join nie wyeliminuje problemu, biorąc pod uwagę, że błędnie zachowa jedno z trzech wystąpień wyniku 3.9. Poprawnym przypadkiem jest uwzględnienie dwóch (2) wystąpień wyniku 3.9biorąc pod uwagę, że faktycznie mamy dwóch (2) uczniów z tym wynikiem, które spełniają nasze kryteria kwerendy.

Wydaje się, że w niektórych przypadkach zapytanie podrzędne jest najbezpieczniejszym sposobem, poza problemami z wydajnością.

 24
Author: pkaramol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-20 13:30:26

Dokumentacja MSDN dla SQL Server mówi

Wiele poleceń Transact-SQL, które zawierają zapytania podrzędne, można alternatywnie sformułować jako joiny. Inne pytania mogą być zadawane tylko za pomocą zapytań podrzędnych. W Transact-SQL, zwykle nie ma różnicy w wydajności między instrukcją zawierającą zapytanie podrzędne a semantycznie równoważną wersją, która nie ma. Jednak w niektórych przypadkach, gdy istnienie musi być sprawdzone, połączenie daje lepszą wydajność. W przeciwnym razie zagnieżdżone zapytanie muszą być przetwarzane dla każdego wyniku zewnętrznego zapytania, aby zapewnić eliminację duplikatów. W takich przypadkach podejście join przyniosłoby lepsze wyniki.

Więc jeśli potrzebujesz czegoś takiego

select * from t1 where exists select * from t2 where t2.parent=t1.id

Spróbuj użyć join zamiast tego. W innych przypadkach nie ma to znaczenia.

Mówię: Tworzenie funkcji dla zapytań podrzędnych eliminuje problem clutttera i pozwala na implementację dodatkowej logiki do zapytań podrzędnych. Dlatego polecam tworzenie funkcji dla zapytań podrzędnych, gdy tylko możliwe.

[1]}bałagan w kodzie to duży problem, a branża stara się go uniknąć od dziesięcioleci.
 22
Author: Uğur Gümüşhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-22 11:50:33

Zgodnie z moją obserwacją, jak dwa przypadki, jeśli tabela ma mniej niż 100,000 rekordów, to połączenie będzie działać szybko.

Ale w przypadku, gdy tabela ma więcej niż 100 000 rekordów, to zapytanie podrzędne jest najlepszym wynikiem.

Mam jedną tabelę, która ma 500 000 rekordów, które utworzyłem poniżej zapytania i jej czas wynikowy jest jak

SELECT * 
FROM crv.workorder_details wd 
inner join  crv.workorder wr on wr.workorder_id = wd.workorder_id;

Wynik: 13.3 Sekund

select * 
from crv.workorder_details 
where workorder_id in (select workorder_id from crv.workorder)

Wynik: 1.65 Sekundy

 18
Author: Vijay Gajera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-01 03:04:08

Uruchom na bardzo dużej bazie danych ze starego CMS Mambo:

SELECT id, alias
FROM
  mos_categories
WHERE
  id IN (
    SELECT
      DISTINCT catid
    FROM mos_content
  );

0 sekund

SELECT
  DISTINCT mos_content.catid,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

~3 sekundy

Wyjaśnienie pokazuje, że badają dokładnie tę samą liczbę wierszy, ale jeden trwa 3 sekundy, a drugi jest prawie natychmiastowy. Morał z tej historii? Jeśli wydajność jest ważna (kiedy nie jest?), spróbuj na wiele sposobów i zobacz, który z nich jest najszybszy.

I...

SELECT
  DISTINCT mos_categories.id,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

0 sekund

Znowu te same wyniki, Ta sama liczba zbadanych wierszy. Zgaduję, że to odrębny mos_content.catid zajmuje znacznie więcej czasu, aby dowiedzieć się, niż odrębne mos_categories.id tak.

 16
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-26 11:28:54

Zapytania podrzędne są zwykle używane do zwracania pojedynczego wiersza jako wartości atomowej, chociaż mogą być używane do porównywania wartości z wieloma wierszami za pomocą słowa kluczowego IN. Są one dozwolone w prawie każdym znaczącym punkcie instrukcji SQL, w tym w liście docelowej, klauzuli WHERE i tak dalej. Jako warunek wyszukiwania można użyć prostego zapytania podrzędnego. Na przykład między parą tabel:

SELECT title 
FROM books 
WHERE author_id = (
    SELECT id 
    FROM authors 
    WHERE last_name = 'Bar' AND first_name = 'Foo'
);

Zauważ, że użycie operatora wartości normalnej na wynikach zapytania podrzędnego wymaga, aby tylko jedno pole musi zostać zwrócony. Jeśli chcesz sprawdzić istnienie pojedynczej wartości w zestawie innych wartości, użyj IN:

SELECT title 
FROM books 
WHERE author_id IN (
    SELECT id FROM authors WHERE last_name ~ '^[A-E]'
);

To oczywiście różni się od powiedzmy a LEFT-JOIN, gdzie chcesz po prostu dołączyć rzeczy z tabeli a i B, nawet jeśli warunek join nie znajduje żadnego pasującego rekordu w tabeli B, itp.

Jeśli martwisz się tylko o szybkość, musisz sprawdzić swoją bazę danych i napisać dobre zapytanie i sprawdzić, czy nie ma żadnej znaczącej różnicy w wydajności.

 13
Author: rkulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-11-26 10:59:06

MySQL version: 5.5.28-0ubuntu0.12.04.2-log

Miałem również wrażenie, że JOIN jest zawsze lepszy niż sub-query w MySQL, ale EXPLAIN jest lepszym sposobem na ocenę. Oto przykład, w którym zapytania podrzędne działają lepiej niż łączniki.

Oto moje zapytanie z 3 pod-zapytaniami:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=43) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=55) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

Wyjaśnij pokazuje:

+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
| id | select_type        | table    | type   | possible_keys                                       | key          | key_len | ref                                             | rows | Extra                    |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
|  1 | PRIMARY            | vrl      | index  | PRIMARY                                             | moved_date   | 8       | NULL                                            |  200 | Using where              |
|  1 | PRIMARY            | l        | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  1 | PRIMARY            | vrlih    | eq_ref | PRIMARY                                             | PRIMARY      | 9       | ranker.vrl.list_id,ranker.vrl.ontology_id,const |    1 | Using where              |
|  1 | PRIMARY            | lbs      | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  4 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  3 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+

To samo zapytanie z JOINs to:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
LEFT JOIN list_tag lt1 ON lt1.list_id = vrl.list_id AND lt1.tag_id = 43 
LEFT JOIN list_tag lt2 ON lt2.list_id = vrl.list_id AND lt2.tag_id = 55 
INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
AND lt1.list_id IS NULL AND lt2.tag_id IS NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

A wyjście to:

+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                       | key          | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | lt3   | ref    | list_tag_key,list_id,tag_id                         | tag_id       | 5       | const                                       | 2386 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.lt3.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | vrlih | ref    | PRIMARY                                             | PRIMARY      | 4       | ranker.lt3.list_id                          |  103 | Using where                                  |
|  1 | SIMPLE      | vrl   | ref    | PRIMARY                                             | PRIMARY      | 8       | ranker.lt3.list_id,ranker.vrlih.ontology_id |   65 | Using where                                  |
|  1 | SIMPLE      | lt1   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index; Not exists         |
|  1 | SIMPLE      | lbs   | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | lt2   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index                     |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+

Porównanie kolumny rows mówi o różnica i zapytanie z połączeniami używa Using temporary; Using filesort.

Oczywiście gdy uruchamiam oba zapytania, pierwsze robi się w 0.02 sek, drugie nie kończy się nawet po 1 min, więc wyjaśnij poprawnie te zapytania.

Jeśli nie mam łącznika wewnętrznego w tabeli list_tag tzn. jeśli usunę

AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL  

Od pierwszego zapytania i odpowiednio:

INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403

Z drugiego zapytania, następnie EXPLAIN zwraca tę samą liczbę wierszy dla obu zapytań i obu tych zapytania działają równie szybko.

 11
Author: arun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-06 19:38:54
  • ogólna zasada jest taka, że połączenia są w większości przypadków szybsze (99%).
  • im więcej tabel danych, tym zapytania podrzędne są wolniejsze.
  • mniej tabel danych ma, zapytania podrzędne mają równoważną prędkość jak dołącza.
  • zapytania podrzędne są prostsze, łatwiejsze do zrozumienia i łatwiejsze do odczytania.
  • Większość frameworków internetowych i aplikacji oraz ich "ORM"i "Active record"generują zapytania z subqueries , ponieważ z subqueries są łatwiejsze do podziału odpowiedzialności, utrzymania kodu itp.
  • dla mniejszych stron internetowych lub aplikacji zapytania podrzędne są w porządku, ale dla większych stron internetowych i aplikacji często będziesz musiał przepisać wygenerowane zapytania do join queries, szczególnie jeśli zapytanie używa wielu zapytań podrzędnych w zapytaniu.

Niektórzy ludzie mówią "niektóre RDBMS może przepisać subquery do join lub join do subquery kiedy myśli, że jeden jest szybszy niż drugi.", ale to stwierdzenie odnosi się do prostych przypadków, z pewnością nie do skomplikowanych zapytań z subqueries , które faktycznie powodują problemy w wydajności.

 11
Author: fico7489,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-04 07:07:52

Zapytania podrzędne mają możliwość obliczania funkcji agregacji w locie. Np. Znajdź minimalną cenę książki i uzyskaj wszystkie książki, które są sprzedawane z tą ceną. 1) Using Subqueries:

SELECT titles, price
FROM Books, Orders
WHERE price = 
(SELECT MIN(price)
 FROM Orders) AND (Books.ID=Orders.ID);

2) używanie łączników

SELECT MIN(price)
     FROM Orders;
-----------------
2.99

SELECT titles, price
FROM Books b
INNER JOIN  Orders o
ON b.ID = o.ID
WHERE o.price = 2.99;
 10
Author: Vlad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-17 12:58:47

Różnica jest widoczna tylko wtedy, gdy druga tabela łączenia ma znacznie więcej danych niż tabela podstawowa. Miałem doświadczenie jak poniżej...

Mieliśmy tabelę użytkowników sto tysięcy wpisów i ich dane członkowskie (przyjaźń) około 300 tysięcy wpisów. To było wspólne oświadczenie, aby zabrać przyjaciół i ich dane, ale z dużym opóźnieniem. Ale to działało dobrze, gdzie nie było tylko niewielka ilość danych w tabeli członkostwa. Kiedy zmieniliśmy go na pod-zapytanie działało dobrze.

Ale w międzyczasie zapytania join działają z innymi tabelami, które mają mniej wpisów niż tabela podstawowa.

Więc myślę, że instrukcje join i sub query działają dobrze i to zależy od danych i sytuacji.

 4
Author: jpk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-16 08:12:19

W dzisiejszych czasach wiele dbs może zoptymalizować zapytania podrzędne i połączenia. Tak więc, po prostu gotto zbadać swoje zapytanie za pomocą wyjaśnić i zobaczyć, który z nich jest szybszy. Jeśli nie ma dużej różnicy w wydajności, wolę używać zapytań podrzędnych, ponieważ są one proste i łatwiejsze do zrozumienia.

 3
Author: Eunwoo Song,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-22 08:06:22

Właśnie myślę o tym samym problemie, ale używam subquery w części FROM. Muszę połączyć i zapytanie z dużych tabel," slave " tabela ma 28 milionów rekordów, ale wynik jest tylko 128 tak mały wynik Duże dane! Używam na nim funkcji MAX ().

Najpierw używam LEFT JOIN, ponieważ myślę, że jest to prawidłowy sposób, mysql może optymalizować itp. Drugi raz tylko do testów, przepisuję, aby wybrać sub-select przeciwko JOIN.

LEFT JOIN czas trwania: 1.12 s Pod-wybierz runtime: 0.06 s

18 razy szybszy subselect niż join! Tylko w adv chokito. subselect wygląda okropnie, ale wynik ...

 1
Author: Karoly Szabo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-23 09:04:21

Zależy to od kilku czynników, w tym od konkretnego zapytania, które uruchamiasz, ilości danych w bazie danych. Subquery uruchamia najpierw wewnętrzne zapytania, a następnie z zestawu wyników ponownie filtruje rzeczywiste wyniki. Podczas gdy w połączeniu uruchamia i produkuje wynik za jednym zamachem.

Najlepszą strategią jest to, że powinieneś przetestować zarówno rozwiązanie join, jak i rozwiązanie subquery, aby uzyskać zoptymalizowane rozwiązanie.

 0
Author: Ayesha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-12 19:15:10

Jeśli chcesz przyspieszyć zapytanie za pomocą join:

Dla "inner join / join", Nie używaj where condition zamiast tego użyj go w" ON " condition. Eg:

     select id,name from table1 a  
   join table2 b on a.name=b.name
   where id='123'

 Try,

    select id,name from table1 a  
   join table2 b on a.name=b.name and a.id='123'

Dla " Left/Right Join", Nie używaj w stanie "ON" , ponieważ jeśli użyjesz left/right join, otrzyma wszystkie wiersze dla dowolnego table.So, nie ma sensu używać go w "On". Więc spróbuj użyć warunku "gdzie"

 -2
Author: sam ruben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-24 12:25:32