Użycie słowa kluczowego DOŁĄCZ lub nie

45

Następujące zapytania SQL są takie same:

SELECT column1, column2
FROM table1, table2
WHERE table1.id = table2.id;

SELECT column1, column2
FROM table1 JOIN table2 
ON table1.id = table2.id;

I z pewnością skutkują tymi samymi planami zapytań na każdym DBMS, którego kiedykolwiek próbowałem.

Ale co jakiś czas czytam lub słyszę opinię, że jedna jest zdecydowanie lepsza od drugiej. Oczywiście twierdzenia te nigdy nie są uzasadnione wyjaśnieniem.

Tam, gdzie pracuję, wydaje się, że druga wersja jest faworyzowana przez większość deweloperów, dlatego też dążę do tego stylu, aby zminimalizować zaskoczenie. Ale w moim sercu naprawdę myślę o tym pierwszym (ponieważ tak właśnie się nauczyłem).

Czy jedna z tych form jest obiektywnie lepsza od drugiej? Jeśli nie, jakie byłyby powody używania jednego nad drugim?

SingleNegationElimination
źródło
1
Dlaczego nie profilować go i powiadomić resztę o wyniku? Ogólnie rzecz biorąc, wydajność znacznie przewyższa preferencje dotyczące stylu.
Demian Brecht
3
„skutkują tymi samymi planami zapytań na każdym DBMS, którego kiedykolwiek próbowałem”. Gdyby to mogło dać odpowiedź pod względem wydajności, zapytałby o nią na stackoverflow.com. Niestety, są to te same zapytania.
SingleNegationElimination
Ach .. Nieodebrane :)
Demian Brecht
2
„Subiektywny” nie oznacza „jaka jest twoja opinia”. Mam edytowany to rodzaj spełniają kryteria określone w tym FAQ .
Aaronaught
Ja też dążę do tego stylu, aby zminimalizować zdziwienie. Myślę, że właśnie odpowiedziałeś na własne pytanie. Niespodzianki są złe.
Pieter B

Odpowiedzi:

60

Uważam, że druga forma jest lepsza. Może dlatego, że tak się nauczyłem, przyznaję, ale mam jeden konkretny powód - rozdzielenie obaw. Umieszczenie pól używanych do łączenia tabel w klauzuli where może prowadzić do trudności w zrozumieniu zapytań.

Na przykład weź następujące zapytanie:

select *
from table1, table2, table3, table4
where table1.id = table2.id
and table2.id = table3.id
and table3.id = table4.id
and table1.column1 = 'Value 1'

Powyższe zapytanie zawiera warunki łączenia tabel i rzeczywiste warunki logiki biznesowej połączone w jedną przestrzeń. Przy dużym zapytaniu może to być bardzo trudne do zrozumienia.

Jednak teraz weź ten kod:

select *
from table1 join table2 on table1.id = table2.id
join table3 on table2.id = table3.id
join table4 on table3.id = table4.id
where table1.column1 = 'Value 1'

W takim przypadku wszystko, co ma związek z tabelami lub ich relacją, jest odizolowane od klauzuli from, podczas gdy rzeczywista logika biznesowa dla ograniczenia zapytań znajduje się w klauzuli where. Myślę, że jest to o wiele bardziej zrozumiałe, szczególnie w przypadku większych zapytań.

Dustin Wilhelmi
źródło
Jest to jedyny rozsądny sposób, aby to zrobić, zwłaszcza gdy miniesz dwa tabele lub potrzebujesz kombinacji lewych, prawych i pełnych połączeń.
aglassman
5
+1 W przypadku „rozdzielenia problemów” połączenia łączą dane, gdzie klauzule dyktują podzbiory danych, którymi jesteś zainteresowany.
39

Składnia złączenia zastąpiła starą składnię przecinków w 1992 r. Obecnie nie ma powodu, aby pisać kod ze składnią przecinków. Nic nie zyskujesz i masz problemy, których po prostu nie masz z jawną składnią.

Po pierwsze, gdy dostajesz bardziej skomplikowane zapytania, bardzo łatwo jest wykonać przypadkowe połączenie krzyżowe, pomijając warunek where. Jest to coś, co jawna składnia łączenia może zapobiec, ponieważ wystąpi błąd składniowy.

Jeśli zamierzasz połączyć krzyżowo, jawna składnia sprzężenia wyjaśni to, gdy w domniemanej składni ktoś przeprowadzający konserwację może założyć, że zapomniałeś dodać klauzuli where.

Następnie pojawia się problem lewych i prawych złączeń, które są problematyczne przynajmniej w niektórych dbach przy użyciu niejawnej składni. Są one przestarzałe w SQL Server i w rzeczywistości nie zwracają poprawnych wyników nawet w starszych wersjach. Żadne zapytanie wymagające połączenia zewnętrznego nie powinno zawierać niejawnej składni w programie SQL Server.

Ponadto widziałem pytania tutaj i na innych stronach, na których zdarzały się złe wyniki, gdy ludzie mieszali niejawne i jawne sprzężenia (na przykład dodając lewe sprzężenie), więc źle jest mieszać je.

Wreszcie wiele osób korzystających z niejawnych połączeń nie rozumie połączeń. Jest to krytyczne zrozumienie, że musisz efektywnie przeszukiwać bazę danych.

HLGEM
źródło
Dziękuję za wyjaśnienie. Kiedy mnie nauczono, pokazano nam obie składnie, ale różnica nie została wyjaśniona. Czasami udało mi się wygenerować zapytania, w których brakuje, gdzie, szczerze mówiąc, zwiększyłoby to ilość pisania, a nie tylko jednoznaczne dołączenie.
awiebe
8

Ha. Zdarzyło mi się znaleźć możliwą odpowiedź na własne pytanie, przeglądając dokumentację PostgreSQL . Podsumowując, co wyjaśnia ta strona, wynikowe zapytanie jest nadal takie samo, ale liczba planów, które optymalizator musi rozważyć, rośnie wykładniczo wraz z liczbą sprzężeń.

Po około sześciu takich połączeniach liczba jest tak duża, że ​​czas na zaplanowanie zapytania może być zauważalny, a po około dziesięciu optymalizator przełączy się z wyczerpującego wyszukiwania planów na wyszukiwanie probabilistyczne i może nie uzyskać optymalnego planu .

Ustawiając parametr czasu wykonywania, możesz poinstruować planistę, aby traktował wyraźnie wymienione połączenia wewnętrzne i krzyżowe inaczej niż sprzężenia niejawne, zmuszając je do początku planu i nie analizując innych opcji.

Warto zauważyć, że domyślne zachowanie jest takie samo w obu przypadkach, a uzyskanie alternatywnych planów wymaga znajomości elementów wewnętrznych dbms i osobliwości danych tabel, aby uzyskać inny wynik

SingleNegationElimination
źródło
2
Jednak nieco źle zrozumiałeś te dokumenty. Po pierwsze, istnieją trzy progi. Jeden zwalnia GEQO, jak wskazałeś; pozostałe dwa (z limitów zwinięcia i dołączenia) powodują, że strugarka pozostaje przy wybieraniu odpowiednich indeksów, zamiast reorganizować kolejność łączenia. Po drugie i co równie ważne, zapytania są przepisywane w miarę ich analizowania. Powoduje to, że pierwsze przykładowe zapytania są analizowane w dokładnie tym samym drzewie zapytań, co drzewo drugie - progi informują PG, czy powinien spróbować zmienić kolejność połączeń, czy nie.
Denis de Bernardy
8

Oto widok teorii zbiorów:

Kiedy używasz przecinka, aby oddzielić dwie (lub więcej) nazwy tabeli, to zamierzasz uzyskać produkt kartezjański. Każdy wiersz „lewego” stołu będzie „dopasowany” (konkatenowany) z rzędem prawego stołu.

Teraz, jeśli napiszesz coś w klauzuli where, będzie to jak nałożenie warunku na tę „konkatenację”, mówiącą, które wiersze „połączyć” z którymi wierszami.

W rzeczywistości jest to „łączenie” wierszy :), a zatem słowo kluczowe join, które pomaga zapewnić bardziej czytelną składnię i jest bardziej zrozumiałe, że „naprawdę” chcesz dołączyć do niektórych wspólnych wartości. Podobne do tego, co @Dustin wyjaśnił powyżej.

Teraz każdy DBMS jest inteligentny, tzn. Nie oblicza najpierw produktu kartezjańskiego, a następnie odfiltrowuje dane (wyjątkowo marnotrawstwo), a raczej robi to na podstawie struktury zapytania. Jedyną rzeczą, o której mogę myśleć, jest to, że kiedy poprosisz o „dołączenie”, to tak, jakby wyraźne było łączenie działania i prawdopodobnie pomaga szybciej uruchomić kod (o ile? Musisz go profilować i zobaczyć), ale w Przypadek oddzielony przecinkami potrzebuje trochę czasu, aby „wymyślić” optymalną strategię. Mogę się mylić, ale zgaduję, jak można to kodować ...

Doktorat
źródło
5

Myślę, że ogólnie lepiej jest użyć instrukcji JOIN w tej sprawie.

Jeśli w przyszłości pojawi się sytuacja, która wymaga zmiany instrukcji z WEJŚCIA WEWNĘTRZNEGO na WEJŚCIE ZEWNĘTRZNE, będzie to znacznie łatwiejsze w przypadku drugiej instrukcji.

Britt Wescott
źródło
3

Każdy RDBMS sprawi, że będą one tym samym pod względem wykonania. Wszystko sprowadza się do tego, czy ktoś jest bardziej czytelny i wyrazisty.

Użyj DOŁĄCZ, aby było jasne, co to jest łączenie złączeń, a jaki jest faktyczny wybór, jak w:

select name, deptname
from people p, departments d
where p.deptid = d.id and p.is_temp = 'Y'

vs.

select name, deptname
from people p
    inner join departments d on p.deptid = d.id
where p.is_temp = 'Y'

Ten drugi przypadek natychmiast wyjaśnia, który jest warunkiem łączenia, a który jest kryterium wyboru.

Andy Lester
źródło
1

Tylko raz widziałem te dwa wyniki w innym zestawie optymalizacji i jeśli pamięć służy, to było w ms-sql2k na naprawdę włochatym zapytaniu. W tym jednym przykładzie stara forma użyta z * = spowodowała około 4x szybszą wydajność. Nikt, w tym nasi technicy Microsoft, nie potrafiłby nigdy wyjaśnić dlaczego. Faceci ze stwardnienia rozsianego opisali to jako błąd. Nigdy więcej tego nie widziałem.

Ponieważ większość RDBMS jest wystarczająco inteligentna, aby nie robić pełnych kartezjanów, największym powodem, dla którego mogę myśleć o nieużywaniu go (poza tym, że jest to amortyzowane) jest to, że większość osób poniżej 30-35 lat, z którymi pracowałem, nigdy nie widziała stara forma wcześniej i strasznie się zagubiła, gdy ją napotka.

Rachunek
źródło
Oczywiście ta składnia dołączenia po lewej nigdy nie zapewniała poprawnych wyników w sposób niezawodny (patrz BOL dla SQL Server 2000), więc nawet gdyby była szybsza, zastąpiłbym ją.
HLGEM
Nigdy tego nie spotkałem, a wyszukiwanie gwiazdką nigdy się nie kończy dobrze, czy masz przykład?
Bill
-1

Stary styl jest przestarzały, nie należy go używać.

Nie powinno być nawet sporu o to, czy ktoś jest lepszy, czy nie. Nowy kod nie powinien używać starej składni.

Pieter B.
źródło
Myślę, że ta odpowiedź tak naprawdę nic nie dodaje, nie mówiąc o tym, dlaczego była przestarzała i nie powinna być używana.
RemcoGerlich,
1
@RemcoGerlich, dlaczego został wycofany, nie jest tutaj omawiany. Dyskutowane jest tutaj, czy użyć starej, czy nowej składni. To, czy jedno jest lepsze od drugiego, czy nie, jest dyskusyjne: nie powinieneś używać starej składni. Dlaczego kwestia jest inna dyskusja. (taki, który został rozstrzygnięty 20 lat temu.)
Pieter B
-4

Jednym z powodów, dla których składnia jest bardziej zwięzła, jest to, że jest ona bardziej zwięzła, więc jeśli czujesz się komfortowo, łatwiej ją odczytać. Myślę, że pełny opis przypadku jest podobny do wypisywania arytmetyki w języku COBOL, np. MULTIPLY A BY B DAJĄC C.

John Bickers
źródło
Downvoters: Czy w tej odpowiedzi jest coś niepoprawnego, czy też po prostu „nie zgadzają się z tobą”?
Adam Libuša,