SQL LEFT join vs wiele tabel on FROM line?

Większość dialektów SQL akceptuje oba poniższe zapytania:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

Teraz oczywiście, gdy potrzebujesz zewnętrznego połączenia, wymagana jest druga składnia. Ale robiąc INNER join dlaczego powinienem preferować drugą składnię od pierwszej (lub odwrotnie)?

Author: Kev, 2009-05-21

11 answers

Stara składnia, zawierająca tylko listę tabel i używanie klauzuli WHERE do określenia kryteriów łączenia, jest przestarzała w większości nowoczesnych baz danych.

To nie tylko na pokaz, stara składnia ma możliwość być niejednoznaczna, gdy używasz zarówno łączników wewnętrznych, jak i zewnętrznych w tym samym zapytaniu.

Pozwól, że podam ci przykład.

Załóżmy, że masz 3 tabele w systemie:

Company
Department
Employee

Każda tabela zawiera liczne wiersze, połączone ze sobą. Masz wiele firm, każda firma może mieć wiele działów, a każdy dział może mieć wielu pracowników.

Ok, więc teraz chcesz zrobić co następuje:

Lista wszystkich firm, i obejmują wszystkie ich działy, i wszystkich ich pracowników. Zauważ, że niektóre firmy nie mają jeszcze żadnych działów, ale upewnij się, że również je uwzględniasz. Upewnij się, że odzyskujesz tylko działy, które mają pracowników, ale zawsze wymieniaj wszystkie firmy.

Więc zrób to:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

Uwaga że ostatni jest wewnętrzny join, aby spełnić kryteria, które chcesz tylko działy z ludźmi.

Ok, więc co teraz? Cóż, problem polega na tym, że zależy to od silnika bazy danych, optymalizatora zapytań, indeksów i statystyk tabel. Pozwól mi wyjaśnić.

Jeśli optymalizator zapytań stwierdzi, że sposobem na to jest najpierw przejęcie firmy, a następnie znalezienie działów, a następnie połączenie wewnętrzne z pracownikami, nie dostaniesz żadnych firm, które nie mieć wydziały.

Powodem tego jest to, że klauzula WHEREokreśla, które wiersze kończą się w wyniku końcowym, a nie poszczególne części wierszy.

I w tym przypadku, ze względu na lewe połączenie, Department.ID kolumna będzie NULL, a zatem jeśli chodzi o wewnętrzne połączenie z pracownikiem, nie ma sposobu, aby spełnić to ograniczenie dla wiersza pracownika, a więc nie pojawi się.

Z drugiej strony, jeśli optymalizator zapytań zdecyduje się zająć działem-pracownikiem Dołącz najpierw, a następnie wykonaj lewe połączenie z firmami, zobaczysz je.

Więc stara składnia jest niejednoznaczna. Nie ma sposobu, aby określić, co chcesz, bez radzenia sobie z podpowiedziami do zapytań, a niektóre bazy danych w ogóle nie mają możliwości.

Wprowadź nową składnię, z tym możesz wybrać.

Na przykład, jeśli chcesz, aby wszystkie firmy, zgodnie z opisem problemu, to jest to, co byś napisał:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

Tutaj zaznaczasz, że chcesz dołączyć do działu-pracownika jako jeden dołączyć, a następnie w lewo dołączyć wyniki tego z firmami.

Dodatkowo, powiedzmy, że chcesz tylko działy, które zawierają literę X w ich nazwie. Ponownie, ze starym stylem, ryzykujesz utratę firmy, jak również, jeśli nie ma żadnych działów z X w nazwie, ale z nową składnią, można to zrobić:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

Ta dodatkowa klauzula jest używana do łączenia, ale nie jest filtrem dla całego wiersza. Więc wiersz może pojawić się z informacjami o firmie, ale może mieć null we wszystkich kolumnach działu i pracowników dla tego wiersza, ponieważ nie ma działu z X w nazwie dla tej firmy. Jest to trudne ze starą składnią.

Dlatego, między innymi, Microsoft przestarzała składnia exterior join, ale nie stara składnia inner join, od SQL Server 2005 i starszych. Jedynym sposobem, aby rozmawiać z bazą danych działającą na Microsoft SQL Server 2005 lub 2008, używając składni outer join w starym stylu, jest ustawienie tej bazy danych w 8.0 tryb zgodności (aka SQL Server 2000).

Dodatkowo stary sposób, rzucając kilka tabel w optymalizator zapytań, z kilkoma klauzulami WHERE, był podobny do powiedzenia "oto jesteś, zrób co możesz". Dzięki nowej składni optymalizator zapytań ma mniej pracy, aby dowiedzieć się, które części idą w parze.

No i proszę. / Align = "left" /
 293
Author: Lasse Vågsæther Karlsen,
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-08-15 10:24:15

Składnia JOIN zachowuje warunki w pobliżu tabeli, do której mają zastosowanie. Jest to szczególnie przydatne, gdy dołączysz do dużej liczby stołów.

Przy okazji, możesz też wykonać zewnętrzne połączenie z pierwszą składnią:

WHERE a.x = b.x(+)

Lub

WHERE a.x *= b.x

Lub

WHERE a.x = b.x or a.x not in (select x from b)
 16
Author: Andomar,
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
2009-05-21 18:56:38

Pierwszy sposób to starszy standard. Druga metoda została wprowadzona w SQL-92, http://en.wikipedia.org/wiki/SQL . pełny standard można obejrzeć pod adresem http://www.contrib.andrew.cmu.edu / ~shadow/sql/sql1992.txt .

Minęło wiele lat, zanim firmy bazodanowe przyjęły standard SQL-92.

Więc powodem, dla którego druga metoda jest preferowana, jest to standard SQL według Komitetu standardów ANSI i ISO.

 10
Author: Dwight T,
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
2009-05-21 20:06:19

Drugi jest preferowany, ponieważ jest znacznie mniej prawdopodobne, że spowoduje przypadkowe połączenie krzyżowe, zapominając o umieszczeniu klauzuli where. Join z klauzulą no on nie sprawdzi składni, an old style join z klauzulą no where nie zawiedzie, wykona połączenie krzyżowe.

DODATKOWO, gdy później będziesz musiał połączyć się w lewo, pomocne dla utrzymania jest, aby wszystkie były w tej samej strukturze. A stara składnia jest nieaktualna od 1992 roku, nadszedł już dawno, aby przestać używać to.

Poza tym odkryłem, że wiele osób, które używają wyłącznie pierwszej składni, nie rozumie joinsów, a zrozumienie joinsów ma kluczowe znaczenie dla uzyskania poprawnych wyników podczas zapytań.

 9
Author: HLGEM,
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
2009-05-21 19:02:38

Zasadniczo, gdy klauzula FROM wyświetla tabele w ten sposób:

SELECT * FROM
  tableA, tableB, tableC

Wynik jest iloczynem krzyżowym wszystkich wierszy w tabelach A, B, C. następnie stosuje się ograniczenie WHERE tableA.id = tableB.a_id, które wyrzuci ogromną liczbę wierszy, a następnie dalej ... AND tableB.id = tableC.b_id i wtedy powinieneś dostać tylko te wiersze, które naprawdę Cię interesują.

DBMSs wie, jak zoptymalizować ten SQL, aby różnica wydajności w stosunku do zapisu tego za pomocą Joinsów była znikoma (jeśli w ogóle). Korzystanie z notacji JOIN sprawia, że SQL statement more readable (IMHO, nie używając joins zamienia wypowiedź w bałagan). Korzystając z iloczynu krzyżowego, musisz podać kryteria przyłączenia w klauzuli WHERE, i to jest problem z zapisem. Wypychasz swoją klauzulę WHERE takimi rzeczami jak

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

Który jest używany tylko w celu ograniczenia iloczynu krzyżowego. Klauzula WHERE powinna zawierać jedynie ograniczenia dla resultset. Jeśli połączysz kryteria table join z ograniczeniami resultset, ty (i inni) znajdziesz swoje zapytanie trudniej czytać. Zdecydowanie powinieneś używać JOINs i zachować klauzulę FROM a klauzulę FROM oraz klauzulę WHERE a WHERE.

 9
Author: Peter Perháč,
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-04-13 10:40:05

Myślę, że na tej stronie jest kilka dobrych powodów, aby przyjąć drugą metodę - używanie jawnych złączeń. Najważniejsze jest jednak to, że gdy kryteria przyłączenia są usuwane z klauzuli WHERE, znacznie łatwiej jest zobaczyć pozostałe kryteria wyboru w klauzuli WHERE.

W naprawdę złożonych wyrażeniach SELECT czytelnikowi znacznie łatwiej jest zrozumieć, co się dzieje.

 6
Author: Alan G,
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-06-25 10:17:55

Składnia SELECT * FROM table1, table2, ... jest ok dla kilku tabel, ale staje się wykładniczo ( niekoniecznie matematycznie dokładne stwierdzenie ) coraz trudniej odczytać, gdy liczba tabel rośnie.

Składnia JOIN jest trudniejsza do napisania (na początku), ale wyjaśnia, jakie kryteria wpływają na które tabele. To znacznie utrudnia popełnienie błędu.

Również, jeśli wszystkie połączenia są wewnętrzne, to obie wersje są równoważne. Jednak w momencie, gdy masz zewnętrzną dołącz w dowolnym miejscu w oświadczeniu, sprawy stają się znacznie bardziej skomplikowane i to praktycznie gwarantuje, że to, co piszesz, nie będzie pytaniem o to, co myślisz, że napisałeś.

 5
Author: Euro Micelli,
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
2009-05-21 19:15:53

Jeśli potrzebujesz zewnętrznego połączenia, druga składnia to NIE zawsze wymagane:

Wyrocznia:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (chociaż został przestarzały w wersji 2000) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x
Ale wracając do twojego pytania. Nie znam odpowiedzi, ale prawdopodobnie jest to związane z faktem, że join jest bardziej naturalne (przynajmniej składniowo) niż dodanie wyrażenia do Gdzie klauzuli, gdy robisz dokładnie to: joining .
 2
Author: Pablo Santa Cruz,
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
2009-05-21 19:33:43

Słyszałem, że wiele osób skarży się, że pierwszy jest zbyt trudny do zrozumienia i że jest niejasny. Nie widzę z tym problemu, ale po tej dyskusji używam drugiego nawet na łączach wewnętrznych dla jasności.

 0
Author: kemiller2002,
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
2009-05-21 18:55:50

Do bazy danych, w końcu są takie same. W niektórych sytuacjach będziesz musiał użyć tej drugiej składni. Ze względu na edytowanie zapytań, które kończą się koniecznością korzystania z niego (dowiadując się, że potrzebujesz lewego połączenia, gdzie masz proste połączenie), a dla spójności, chciałbym wzorzec tylko na 2. metodzie. Ułatwi to czytanie zapytań.

 0
Author: Jeff Ferland,
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
2009-05-21 18:56:19

Cóż, pierwsze i drugie zapytania mogą dać różne wyniki, ponieważ lewe połączenie zawiera wszystkie rekordy z pierwszej tabeli, nawet jeśli nie ma odpowiednich rekordów w prawej tabeli.

 0
Author: Gavin H,
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
2009-05-21 18:56:50