Czy jest jakaś istotna różnica między zapytaniami połączonymi klauzulami WHERE a zapytaniami używającymi rzeczywistego JOIN?

32

W Learn SQL the Hard Way (ćwiczenie szóste) autor przedstawia następujące zapytanie:

SELECT pet.id, pet.name, pet.age, pet.dead
    FROM pet, person_pet, person
    WHERE
    pet.id = person_pet.pet_id AND
    person_pet.person_id = person.id AND
    person.first_name = "Zed";

a następnie mówi, że:

Istnieją inne sposoby na uruchomienie tego rodzaju zapytań, zwane „złączeniami”. Na razie unikam tych koncepcji, ponieważ są niesamowicie mylące. Po prostu trzymaj się tego sposobu łączenia na razie i ignoruj ​​ludzi, którzy próbują powiedzieć ci, że jest to jakoś wolniejsze lub „niskiej klasy”.

Czy to prawda? Dlaczego lub dlaczego nie?

Robert Harvey
źródło
3
Nie sądzę, że tak jest, ale możesz spróbować wykonać WYJAŚNIENIE, aby sprawdzić, czy jest jakaś różnica w wykonywaniu zapytania.
GrandmasterB
6
Chciałbym wskazać sprzeczne sygnały pracy z „Hard Way” w tytule pomijając koncepcję „ponieważ są one niesamowicie mylące”. Ale może moja koncepcja tego, jak powinna wyglądać „trudna droga”, jest błędna. Ale znowu może nie.
Mindwin
7
JOIN bardzo ładnie przenosi intencję (łączenie tabel), co pozostawia WHERE część dla rzeczywistych filtrów i sprawia, że ​​jest to trochę łatwiejsze do odczytania. (oprócz wielu innych implikacji)
00 czerwca
2
Uczysz się SQL na własnej skórze, jeśli autorowi nie przeszkadza pisanie prostych połączeń! Jak mówi ThomasS, używając JOIN, intencje są jaśniejsze, a klauzule WHERE stają się znacznie prostsze. Również użycie JOIN lepiej ilustruje teorię zbiorów, która leży u podstaw SQL.
Daniel Hollinrake
1
Nie jestem pewien, co sądzę o czymś, co rzekomo cię czegoś nauczy, mówiąc: „Ale hej, pomińmy tę podstawową koncepcję, ponieważ to banany craaazzzyyyy”. Myślę, że skończyłbym w poszukiwaniu innego źródła do nauki. W pewnym momencie będziesz musiał wykonać połączenia zewnętrzne i połączenia krzyżowe i powinieneś wiedzieć, jak je wykonać.
Maurice Reeves

Odpowiedzi:

23

Dzięki podejściu autora nauczanie POŁĄCZEŃ ZEWNĘTRZNYCH stanie się znacznie trudniejsze. Klauzula ON w INNER JOIN nigdy nie była dla mnie oszałamiająca jak wiele innych rzeczy. Może dlatego, że nigdy nie nauczyłem się starego sposobu. Chciałbym pomyśleć, że istnieje powód, dla którego się go pozbyliśmy i że nie było to zadowolone z siebie i nazwać tę metodę niską klasą.

To prawda w bardzo wąskim scenariuszu, który autor stworzył:

  • Taki podstawowy poziom SQL, że użycie ON jest złożone
  • Tylko biorąc pod uwagę DOŁĄCZ / WEWNĘTRZNE DOŁĄCZENIE, a nie jakiekolwiek ZEWNĘTRZNE DOŁĄCZENIE
  • Izolowany koder, który nie musi czytać kodu innych osób, ani nie ma osób z doświadczeniem w korzystaniu z ON podczas odczytu / używania ich kodu.
  • Nie wymaga skomplikowanych zapytań z dużą ilością: tabel, jeśli jest, ale jest i lub.

Myślę, że w ramach postępu nauczania łatwiej jest go rozbić i osiągnąć naturalny postęp:

Select * from table
select this, something, that from table
select this from table where that = 'this'
select this from table join anothertable on this.id = that.thisid

Pojęcia łączenia i filtrowania tabel nie są tak naprawdę takie same. Nauka prawidłowej składni będzie miała teraz więcej przeniesień, gdy nauczysz się ŁĄCZNIKÓW ZEWNĘTRZNYCH, chyba że autor zamierza uczyć przestarzałych / przestarzałych rzeczy, takich jak: *= or =* .

JeffO
źródło
5
Powodem dodania instrukcji JOIN było to, że nie było standardu wyrażania sprzężeń zewnętrznych, dlatego każdy dostawca bazy danych miał własną „specjalną” (niezgodną) składnię. IIRC Oracle miał *=lub =*wskazuje lewe lub prawe sprzężenia zewnętrzne, inne użyłem tylko obsługiwane lewe połączenia zewnętrzne za pomocą |=operatora.
TMN
1
@TMN IIRC Oracle wykorzystało, +=a może tak było =+. Wierzę, że *=był Transact-SQL (Sybase i później MS-SQL). Wciąż dobra uwaga.
David
1
To zaczyna się komplikować (IMHO), kiedy masz połączenie połączeń wewnętrznych i zewnętrznych. W tego typu sytuacjach przyznam się, że czasami wracam do „niskiej klasy” techniki wykonywania połączeń w WHEREklauzuli. (Słyszałem, że jest to połączenie theta , ale nie jestem pewien, czy to prawda).
David
Operatory IIRC, takie jak „większe niż” lub „równe”, były czasami nazywane „operatorami theta”, ale wyszukiwanie w Google prowadzi do pewnej operacji na rachunku różniczkowym.
Walter Mitty,
12

To, czy będzie wolniejsze, zależy od Optymalizatora zapytań i od tego, jak usprawnia ono zapytanie (to, co piszesz, nie jest tak naprawdę wykonywane). Jednak dużym problemem związanym z tym cytatem jest to, że całkowicie ignoruje on fakt, że istnieją różne rodzaje złączeń, które działają zupełnie inaczej. Na przykład to, co się mówi, jest (teoretycznie) prawdziwe inner joins, ale nie jest prawdziwe dla outer joins( left joinsi right joins).

Locke
źródło
9
+1 Dla innych rodzajów złączeń. Większość moich łączy to albo INNER JOINalbo LEFT OUTER JOIN. Nie są „szalenie mylące”. SQL może być niesamowicie mylący, ale to nie jest tego przykład.
mgw854
off topic ale powinno być oświadczenie różne rodzaje przyłączyć s lub rodzajów przyłączyć ?
user1451111
9

Autor przedstawia prosty przypadek, w którym można zastosować starą lub nową składnię. Nie zgadzam się z jego stwierdzeniem, że złączenia są niesamowicie mylące, ponieważ łączenie tabel jest podstawową koncepcją zapytań SQL. Być może więc autor powinien poświęcić trochę czasu na wyjaśnienie, jak działa JOINS przed wypowiedzeniem wyrażonego zdania, a także na przykładzie zapytania z wieloma tabelami.

Należy użyć nowszej składni. Głównym argumentem jest to, że twoje zapytanie będzie miało:

  • Wybierz kryteria
  • Dołącz do kryteriów
  • Filtruj kryteria

Przy użyciu starego stylu łączone są kryteria łączenia i filtrowania, co w bardziej skomplikowanych przypadkach może prowadzić do zamieszania.

Można również uzyskać produkt kartezjański, zapominając o kryteriach łączenia w klauzuli filtrującej:

 person_pet.person_id = person.id

używając starszej składni.

Użycie nowszej składni określa również sposób łączenia, co jest ważne, czy chcesz INNER, LEFT OUTER itp., Więc jest bardziej wyraźne w odniesieniu do składni JOIN, która IMHO zwiększa czytelność dla osób, które nie znają łączenia tabel.

Jon Raynor
źródło
5

Nie powinno być, parser zapytań powinien generować równoważną wewnętrzną reprezentację dla równoważnych zapytań, niezależnie od tego, jak zostały napisane. Autor po prostu używa składni sprzed SQL-92, dlatego wspomina, że ​​może to być postrzegane jako „staromodne” lub „niskiej klasy”. Analizator składni i optymalizator powinny wewnętrznie wygenerować ten sam plan zapytań.

TMN
źródło
5

W ten sposób nauczyłem się SQL, w tym *=składni sprzężeń zewnętrznych. Dla mnie było to bardzo intuicyjne, ponieważ wszystkie relacje miały równy priorytet i lepiej zadawały pytania w postaci szeregu pytań: Czego chcesz? Skąd chcesz? Który chcesz?

Wykonując joinskładnię, silniej zaburza proces myślenia o relacjach. Osobiście uważam, że kod jest znacznie mniej czytelny, a tabele i relacje są ze sobą powiązane.

Przynajmniej w MSSQL nie ma znaczącej różnicy w wydajności zapytań, przy założeniu, że używasz tej samej kolejności łączenia. To powiedziawszy, istnieje jeden wyraźny, ogromny problem z uczeniem się (i używaniem) SQL w ten sposób. Jeśli zapomnisz o jednym ze swoich relacji, otrzymasz nieoczekiwane efekty krzyżowe. Które w bazie danych o dowolnym nietrywialnym rozmiarze są wyjątkowo drogie (i niebezpieczne dla osób, które nie wybierają!). Znacznie trudniej jest zapomnieć o relacji podczas korzystania ze joinskładni stylu.

Telastyn
źródło
7
Jest to relacyjna baza danych, więc relacje są bardzo ważne dla zapytania. Osobiście uważam, że znacznie trudniej jest znaleźć zapytanie, które łączy prawdziwe filtry (foo.x = 5) ze relacjami (foo.x = bar.x). Silnik może łatwo zoptymalizować to do złączenia, ale człowiek zasadniczo musi się z tym zastanowić rząd po rzędzie, w przeciwieństwie do zestawów i podzbiorów.
Aaronaught
4

Należy wziąć pod uwagę dwa różne aspekty: wydajność i łatwość konserwacji / czytelność .

Konserwowalność / czytelność

Wybrałem inne zapytanie, ponieważ uważam, że jest to lepszy / gorszy przykład niż pierwotne zapytanie, które opublikowałeś.

Co dla ciebie wygląda lepiej i jest bardziej czytelne?

select
    e.LoginID,
    DepartmentName = d.Name
from HumanResources.Employee e
inner join HumanResources.EmployeeDepartmentHistory edh
on e.BusinessEntityID = edh.BusinessEntityID
inner join HumanResources.Department d
on edh.DepartmentID = d.DepartmentID
where d.Name = 'Engineering';

Lub...

select
    e.LoginID,
    DepartmentName = d.Name
from HumanResources.Employee e, 
HumanResources.EmployeeDepartmentHistory edh,
HumanResources.Department d
where e.BusinessEntityID = edh.BusinessEntityID
and edh.DepartmentID = d.DepartmentID
and d.Name = 'Engineering';

Dla mnie osobiście pierwszy jest dość czytelny. Widzisz, że łączymy tabele INNER JOIN, co oznacza, że ​​ściągamy wiersze, które pasują do kolejnej klauzuli łączenia (tj. „Dołącz Pracownik do EmployeeDepartmentHistory na BusinessEntityID i dołącz te wiersze”).

Ten ostatni przecinek nic dla mnie nie znaczy. Zastanawiam się, co robisz z tymi wszystkimi WHEREpredykatami klauzul.

Ten pierwszy czyta więcej, jak myśli mój mózg. Patrzę na SQL przez cały dzień i przecinki dla złączeń. Co prowadzi mnie do następnego punktu ...

Istnieją inne sposoby na uruchomienie tego rodzaju zapytań o nazwie „dołączenia”

Wszystkie są złączeniami. Nawet przecinki są złączeniem. Fakt, że autor ich nie nazywa, jest rzeczywiście ich upadkiem ... nie jest oczywiste. To powinno być oczywiste. Dołączasz do danych relacyjnych, niezależnie od tego, czy określisz, JOINczy ,.

Wydajność

Z pewnością będzie to zależne od RDBMS. Mogę mówić tylko w imieniu Microsoft SQL Server. Pod względem wydajności są one równoważne. Skąd wiesz? Uchwyć plany po wykonaniu i zobacz, co dokładnie robi SQL Server dla każdej z tych instrukcji:

wprowadź opis zdjęcia tutaj

Na powyższym obrazku podkreśliłem, że używam obu zapytań jak powyżej, różniących się tylko wyraźnymi znakami dla złączenia ( JOINvs ,). SQL Server robi dokładnie to samo.

Podsumowanie

Nie używaj przecinków. Używaj wyraźnych JOINinstrukcji.

Thomas Stringer
źródło
Nauczyłem się INNER JOIN na długo, zanim zdałem sobie sprawę, że wariant z klauzulami WHERE jest równoważny, a oba twoje przykłady wyglądają dla mnie bardzo czytelnie. Ten z GDZIEMI i przecinkami może być jeszcze bardziej czytelny. Myślę, że tam, gdzie spada, są duże, złożone zapytania, a nie te stosunkowo proste.
Robert Harvey
Chodzi o to, że myślenie, że odmiana przecinka nie jest łączeniem relacyjnym, wcale nie jest poprawne.
Thomas Stringer,
Myślę, że nieprawidłowo interpretujesz przecinki jako złączenia. Przecinki po prostu oddzielają tabele; to GDZIE warunki tworzą połączenia, a nie przecinki.
Robert Harvey
1
Zdecydowanie mogę powiedzieć, że w klauzulach predykatów nic się nie łączy. Myślę, że niepoprawnie interpretujesz konstrukcje zapytania relacyjnego. Czy próbowałeś połączyć przecinek bez klauzul WHERE? Wciąż działa. To połączenie kartezjańskie. Jak myślisz, co zyskujesz używając przecinków? Nie mów, że próbujesz ratować postacie.
Thomas Stringer
1
Powiedziałbym, że pierwszy jest lepszy, ponieważ twoje intencje są jaśniejsze. Jest znacznie mniej dwuznaczności.
Daniel Hollinrake
4

Nie, to wcale nie jest prawda. Autor ustawia czytelników na zamieszanie i zachęca do programowania kultowego, które pozwala uniknąć bardzo dużej różnicy strukturalnej między standardową składnią a tym starszym wariantem, który preferuje. W szczególności zaśmiecona klauzula WHERE utrudnia ustalenie, co sprawia, że ​​jego zapytanie jest wyjątkowe.

Jego przykład prowadzi czytelnika do wygenerowania mentalnej mapy jego znaczenia, która ma strasznie dużo bałaganu.

SELECT pet.id, pet.name, pet.age, pet.dead
    FROM pet, person_pet, person
    WHERE
    pet.id = person_pet.pet_id AND
    person_pet.person_id = person.id AND
    person.first_name = "Zed";

Z grubsza powyższe jest:

Uzyskaj identyfikator zwierzaka, NAZWĘ, WIEK i ŚMIERĆ dla wszystkich zwierząt domowych, person_pet i osób, w przypadku których identyfikator zwierzaka pasuje do identyfikatora pet_id person_pet, a identyfikator person_id tego rekordu pasuje do identyfikatora person_id osoby, której FIRST_NAME ma wartość „Zed”

Przy takiej mapie mentalnej czytelnik (z jakiegoś powodu ręcznie piszący SQL) może bardzo łatwo popełnić błąd, prawdopodobnie pomijając jedną lub więcej tabel. A czytnik tak napisanego kodu będzie musiał pracować ciężej, aby dowiedzieć się dokładnie, co autor SQL próbuje zrobić. („Trudniejsze” jest na poziomie czytania SQL z podświetlaniem składni lub bez, ale wciąż jest to różnica większa niż zero).

Jest powód, dla którego JOIN są powszechne, i to jest stara, klasyczna „canard seperation of koncern”. W szczególności w przypadku zapytania SQL istnieje dobry powód, aby rozdzielić strukturę danych i sposób ich filtrowania.

Jeśli zapytanie jest napisane czystsze, takie jak

SELECT pet.id, pet.name, pet.age
FROM pet
  JOIN person_pet ON pet.id = person_pet.pet_id
  JOIN person ON person.id = person_pet.person_id
WHERE 
  person.first_name = "Zed";

Następnie czytelnik ma wyraźniejsze rozróżnienie między składnikami tego, o co jest proszony. Filtr wyróżniający tego zapytania jest oddzielony od tego, w jaki sposób jego komponenty odnoszą się do siebie, a niezbędne komponenty każdej relacji znajdują się bezpośrednio tam, gdzie są wymagane.


Oczywiście żaden nowoczesny system bazy danych nie powinien widzieć znaczącej różnicy między tymi dwoma stylami. Ale jeśli wydajność bazy danych byłaby jedyną kwestią, zapytanie SQL nie zawierałoby białych znaków ani wielkich liter.

DougM
źródło
2
Ponieważ kilkakrotnie słyszałem ten refren, pozwólcie mi grać w adwokata diabła. Learn X the Hard Way polega na technicznej głębi; każdy, kto dobrze zna SQL, naprawdę powinien wiedzieć, że oba podejścia są równoważne pod względem produkowanych przez nich wyników.
Robert Harvey
1
Widzę to, ale autor nie twierdzi po prostu, że są odpowiednikami porządnego serwera SQL; twierdzą, że użycie JOIN jest „mylące”, co jest ścieżką, na którą czeka brudny kod. („Nie, nie używaj LINQ, po prostu napisz ręcznie instrukcję FOR.” „Kompilator nie dba o to, co nazywam tą metodą, więc nie ma powodu, aby nie nazywać jej FN1”)
DougM
3

Facet popełnia klasyczny błąd. Próbuje uczyć abstrakcyjnej koncepcji z konkretną implementacją. Jak tylko to zrobisz, wpadniesz w taki bałagan.

Powinien był najpierw nauczyć podstawowych pojęć dotyczących bazy danych, a następnie pokazać SQL jako jeden ze sposobów ich opisu.

Połączenia lewy i prawy, można argumentować, że nie mają one większego znaczenia. Outer Join, możesz użyć starej *=i =*składni.

Teraz możesz argumentować, że składnia jest prostsza, ale tylko dla prostych zapytań. Gdy tylko zaczniesz próbować wykonać złożone zapytanie w tej wersji, możesz wpaść w straszny bałagan. „Nowa” składnia nie została wprowadzona, aby można było wykonywać złożone zapytania, było to więc wykonywanie złożonych zapytań w sposób czytelny, a zatem możliwy do utrzymania.

Tony Hopkinson
źródło
3
„Learn X the Hard Way” to inne podejście do uczenia się. Piszesz kod, a potem rozumiesz go później.
Robert Harvey
7
@RobertHarvey To nie jest inne podejście do uczenia się, jest to standardowe. Później dzieje się to tylko wtedy, gdy zdarza się, że nadal jesteś na miejscu, gdy koła odpadają. zajmowało się zbyt wieloma osobami piszącymi SQL, które uważają, że tabela jest prostokątnym układem komórek, aby mieć jakiekolwiek zaufanie do tej metody.
Tony Hopkinson,
2

Przykład jest równoznaczny z prostym przeformułowaniem z wewnętrznymi JOIN. Różnica polega wyłącznie na dodatkowych możliwościach, na które pozwala składnia JOIN. Na przykład możesz określić kolejność przetwarzania kolumn dwóch tabel; patrz np . https://stackoverflow.com/a/1018825/259310 .

Otrzymaną mądrością jest, w razie wątpliwości, pisanie zapytań w sposób, który czyni je bardziej czytelnymi. Ale to, czy łatwiej jest czytać POŁĄCZENIA lub GDZIE, wydaje się być kwestią osobistych preferencji i dlatego obie formy są tak rozpowszechnione.

Kilian Foth
źródło
Dobra odpowiedź, jednak to, czy użyjesz WHEREklauzuli, czy umieścisz w JOINinstrukcji, może faktycznie wpłynąć na wydajność w zależności od Optymalizatora zapytań. Widziałem to więcej niż jeden raz.
Locke,
Moje wrażenia z wpływu na wydajność są następujące: niejawne sprzężenia umożliwią optymalizatorowi zapytania więcej opcji optymalizacji zapytania, co może wydawać się dobrą rzeczą, ale może stanowić problem. W szczególności optymalizator zapytań może dostroić zapytanie w jeden sposób podczas opracowywania, a drugi w produkcji. Optymalizator może być oszukiwany w strojeniu, które zmniejsza wydajność. Moje zalecenie to użycie jawnej składni złączenia ORAZ potwierdzenie, że łączenie używa kolumn, które mają indeksy, dzięki czemu wydajność jest przewidywalna.
Michael Potter
2

Kiedy nauczyłem się SQL, formularze INNER JOIN, LEFT JOIN itp. Nie istniały. Jak już stwierdzono w innych odpowiedziach, różne dialekty SQL miały zaimplementowane sprzężenia zewnętrzne przy użyciu składni idiosynkratycznej. To uszkodziło przenośność kodu SQL. Ponowne połączenie języka wymagało pewnej zmiany, a LEWE DOŁĄCZENIE itp. Było tym, na czym się zdecydowali.

Prawdą jest, że dla każdego DOŁĄCZENIA WEWNĘTRZNEGO można zapisać równoważne połączenie przecinkiem z warunkiem złączenia w klauzuli WHERE. Przejście od polubienia starej formy do preferowania nowej zajęło mi trochę czasu. Najwyraźniej autor Learning SQL the Hard Way nadal uważa, że ​​stary sposób jest łatwiejszy.

Czy są jakieś różnice? Cóż, tak, są. Po pierwsze, INNER JOIN z klauzulą ​​ON ujawnia intencje autora wyraźniej niż połączenie starego stylu. Fakt, że klauzula ON jest w rzeczywistości warunkiem łączenia, a nie jakimkolwiek innym ograniczeniem, jest bardziej oczywisty. To sprawia, że ​​kod korzystający z INNER JOIN jest łatwiejszy do nauczenia się podczas czytania niż stary styl. Jest to ważne przy utrzymywaniu kodu innej osoby.

Druga różnica polega na tym, że nowy styl nieznacznie ułatwia optymalizatorowi zapytań znalezienie zwycięskiej strategii. To bardzo mały efekt, ale jest prawdziwy.

Trzecia różnica polega na tym, że kiedy nauczysz się używać INNER JOIN (lub zwykłego JOIN), łatwiej jest nauczyć się LEFT JOIN itp.

Poza tym nie ma żadnej istotnej różnicy.

Walter Mitty
źródło
0

To zależy, czy myślisz w kategoriach zbiorów i logiki formalnej .....

Jeśli tego nie zrobisz, nieużywanie słowa kluczowego „Join” ułatwia przejście z logiki formalnej do SQL.

Ale jeśli jak 99% ludzi, nie cieszyłeś się logiką formalną z matematyki, to słowo kluczowe Join jest łatwiejsze do nauczenia się. SQL był prezentowany na uniwersytecie jako inny sposób zapisywania formalnych zapytań logicznych ...

Ian
źródło