Database
 sql >> Baza danych >  >> RDS >> Database

Twój kompletny przewodnik po złączach SQL:OUTER JOIN – część 2

Połączenie zewnętrzne jest dziś w centrum uwagi. A to jest druga część Twojego najlepszego przewodnika po sprzężeniach SQL. Jeśli przegapiłeś część 1, oto link.

Wygląda na to, że zewnętrzne jest przeciwieństwem wewnętrznego. Jeśli jednak rozważysz w ten sposób sprzężenie zewnętrzne, będziesz zdezorientowany. Co więcej, nie musisz wpisywać słowa zewnętrzny w twojej składni jawnie. To opcjonalne!

Ale zanim zagłębimy się w temat, omówmy wartości null dotyczące złączeń zewnętrznych.

Null i OUTER JOIN

Kiedy łączysz 2 tabele, jedna z wartości z każdej tabeli może być pusta. W przypadku INNER JOIN rekordy z wartościami null nie będą pasować i zostaną odrzucone i nie pojawią się w zestawie wyników. Jeśli chcesz uzyskać rekordy, które nie pasują, jedyną opcją jest OUTER JOIN.

Wracając do antonimów, czy nie jest to przeciwieństwo INNER JOIN? Nie do końca, jak zobaczysz w następnej sekcji.

Wszystko o SQL Server OUTER JOIN

Zrozumienie sprzężeń zewnętrznych zaczyna się od danych wyjściowych. Oto pełna lista tego, czego możesz się spodziewać:

  • Wszystkie rekordy, które pasują do warunku złączenia lub predykatu. Jest to wyrażenie zaraz po słowie kluczowym ON, podobnie jak w wyniku INNER JOIN. Nazywamy ten problem rzędem wewnętrznym .
  • Wartości inne niż NULL z lewej tabela z pustymi odpowiednikami z prawej stół. Nazywamy ten problem rzędami zewnętrznymi .
  • Wartości inne niż NULL z prawej tabela z pustymi odpowiednikami z lewej stół. To kolejna forma zewnętrznych rzędów.
  • Wreszcie może to być połączenie wszystkich rzeczy opisanych powyżej.

Dzięki tej liście możemy powiedzieć, że OUTER JOIN zwraca wiersze wewnętrzne i zewnętrzne .

  • Inner – ponieważ dokładne wyniki INNER JOIN mogą być zwrócone.
  • Zewnętrzne – ponieważ zewnętrzne rzędy również mogą być zwrócone.

To jest różnica w stosunku do INNER JOIN.

WEWNĘTRZNE ŁĄCZENIA ZWROT TYLKO WEWNĘTRZNE RZĘDY. POŁĄCZENIA ZEWNĘTRZNE MOGĄ ZWRACAĆ WEWNĘTRZNE I ZEWNĘTRZNE RZĘDY

Zauważ, że użyłem słów „może być” i „może być”. To zależy od Twojej klauzuli WHERE (lub jeśli kiedykolwiek dołączysz klauzulę WHERE), czy zwraca ona zarówno wiersze wewnętrzne, jak i/lub zewnętrzne.

Ale na podstawie instrukcji SELECT możesz określić, która jest tabelą lewą, czy prawą ? Dobre pytanie!

Jak się dowiedzieć, który stół jest lewy, a który prawy w złączeniu?

Możemy odpowiedzieć na to pytanie przykładami:

SELECT *
FROM Table1 a
LEFT OUTER JOIN Table2 b on a.column1 = b.column1

W powyższym przykładzie Tabela1 to lewa tabela, a Tabela2 to właściwy stół. A teraz zróbmy inny przykład. Tym razem jest to proste łączenie wielokrotne.

SELECT *
FROM Table1 a
LEFT OUTER JOIN Table2 b on a.column1 = b.column1
LEFT OUTER JOIN Table3 c on b.column2 = c.column1

W takim przypadku, aby wiedzieć, co jest w lewo, a co w prawo, pamiętaj, że połączenie działa na 2 stołach.

Tabela1 to nadal lewy stół, a Tabela2 to właściwy stół. Odnosi się to do dołączania do 2 stołów:Stół1 i Tabela2 . A co z dołączeniem do Tabela2 i Tabela3 ? Tabela2 staje się tabelą po lewej stronie, a Tabela3 to właściwy stół.

Jeśli dodamy czwartą tabelę, Tabela3 staje się tabelą po lewej stronie, a Tabela4 to właściwy stół. Ale na tym się nie kończy. Możemy dołączyć do innego stołu do Stołu1 . Oto przykład:

SELECT *
FROM Table1 a
LEFT OUTER JOIN Table2 b on a.column1 = b.column1
LEFT OUTER JOIN Table3 c on b.column2 = c.column1
LEFT OUTER JOIN Table4 d on c.column1 = d.column2
LEFT OUTER JOIN Table5 e on a.column2 = e.column1

Tabela1 to lewa tabela, a Tabela5 to właściwy stół. Możesz zrobić to samo z innymi stołami.

Dobra, wróćmy do powyższej listy oczekiwanych wyników. Możemy również wyprowadzić z nich typy złączeń zewnętrznych.

Rodzaje połączeń zewnętrznych

Istnieją 3 typy w oparciu o wyjścia OUTER JOIN.

LEWE POŁĄCZENIE ZEWNĘTRZNE (LEWE POŁĄCZENIE)

LEFT JOIN zwraca wewnętrzne wiersze + wartości inne niż NULL z lewego tabela z pustymi odpowiednikami prawej tabeli. Dlatego jest to LEFT JOIN, ponieważ lewa tabela jest dominującą z dwóch tabel w ramach złączenia, które mają wartości inne niż null.

LEWE ZŁĄCZENIE ZEWNĘTRZNE, PRZYKŁAD 1
-- Return all customerIDs with orders and no orders

USE AdventureWorks
GO

SELECT
 c.CustomerID
,soh.OrderDate
FROM Sales.Customer c
LEFT OUTER JOIN Sales.SalesOrderHeader soh ON c.CustomerID = soh.CustomerID 

W powyższym przykładzie Klient to lewa tabela, a SalesOrderHeader to właściwy stół. Wynik zapytania to 32 166 rekordów – zawiera zarówno rzędy wewnętrzne, jak i zewnętrzne. Możesz zobaczyć jego część na rysunku 1:

Załóżmy, że chcemy zwrócić tylko zewnętrzne wiersze lub klientów bez zamówień. Aby to zrobić, dodaj klauzulę WHERE, aby zawierała tylko wiersze z wartościami null z SalesOrderHeader .

SELECT
 c.CustomerID
,soh.OrderDate
FROM Sales.Customer c
LEFT OUTER JOIN Sales.SalesOrderHeader soh ON c.CustomerID = soh.CustomerID
WHERE soh.SalesOrderID IS NULL

Otrzymany zestaw wyników to 701 rekordów . Wszystkie z nich jak null DataZamówienia z rysunku 1.

Jeśli otrzymam tylko wiersze wewnętrzne, wynikiem będzie 31 465 rekordów . Mogę to zrobić, zmieniając klauzulę WHERE tak, aby zawierała te SalesOrderID które nie są zerowe. Lub mogę zmienić sprzężenie na INNER JOIN i usunąć klauzulę WHERE.

Aby sprawdzić, czy wyewidencjonowano z wyjścia pierwszego przykładu bez klauzuli WHERE, zsumujmy rekordy.

Wewnętrzne rzędy Rzędy zewnętrzne Łączna liczba wierszy
31 465 rekordów 701 rekordów 32 166 rekordów

Z powyższej sumy wierszy z 32 166 rekordami widać, że sprawdza się z pierwszymi przykładowymi wynikami. Pokazuje to również, jak działa LEFT OUTER JOIN.

LEWE ZŁĄCZENIE ZEWNĘTRZNE, PRZYKŁAD 2

Tym razem przykładem jest sprzężenie wielokrotne. Zauważ również, że zrezygnowaliśmy ze słowa kluczowego OUTER.

-- show the people with and without addresses from AdventureWorks
USE AdventureWorks
GO

SELECT
 P.FirstName
,P.MiddleName
,P.LastName
,a.AddressLine1
,a.AddressLine2
,a.City
,adt.Name AS AddressType
FROM Person.Person p
LEFT JOIN Person.BusinessEntityAddress bea ON P.BusinessEntityID = bea.BusinessEntityID
LEFT JOIN Person.Address a ON bea.AddressID = a.AddressID
LEFT JOIN person.AddressType adt ON bea.AddressTypeID = adt.AddressTypeID 

Wygenerował 19 996 rekordów. Możesz sprawdzić część danych wyjściowych na rysunku 2 poniżej. Rekordy o wartości null AddressLine1 są rzędami zewnętrznymi. Nad nim znajdują się wewnętrzne rzędy.

PRAWE POŁĄCZENIE ZEWNĘTRZNE (PRAWE POŁĄCZENIE)

RIGHT JOIN zwraca wewnętrzne wiersze + wartości inne niż NULL z prawego tabeli z pustymi odpowiednikami lewej tabeli.

PRAWE ZŁĄCZENIE ZEWNĘTRZNE, PRZYKŁAD 1
-- From the product reviews, return the products without product reviews
USE AdventureWorks
GO

SELECT
P.Name
FROM Production.ProductReview pr
RIGHT OUTER JOIN Production.Product p ON pr.ProductID = p.ProductID
WHERE pr.ProductReviewID IS NULL 

Rysunek 3 przedstawia 10 z 501 rekordów w zestawie wyników.

W powyższym przykładzie ProductReview to lewa tabela, a Produkt to właściwy stół. Ponieważ jest to RIGHT OUTER JOIN, zamierzamy uwzględnić wartości inne niż NULL z prawej tabeli.

Jednak wybór pomiędzy LEFT JOIN lub RIGHT JOIN zależy od Ciebie. Czemu? Ponieważ możesz wyrazić zapytanie, czy to LEFT, czy RIGHT JOIN, i uzyskać te same wyniki. Spróbujmy z LEWYM JOIN.

-- return the products without product reviews using LEFT OUTER JOIN
USE AdventureWorks
GO

SELECT
P.Name
FROM Production.Product p
LEFT OUTER JOIN Production.ProductReview pr ON pr.ProductID = p.ProductID
WHERE pr.ProductReviewID IS NULL

Spróbuj wykonać powyższe, a uzyskasz taki sam wynik, jak na rysunku 3. Ale czy sądzisz, że Optymalizator zapytań potraktuje je inaczej? Dowiedzmy się w planie wykonania obu na rysunku 4.

Jeśli jesteś w tym nowy, w planie wykonania jest kilka niespodzianek.

  1. Diagramy wyglądają tak samo i są następujące:wypróbuj Porównaj plan pokazowy , a zobaczysz ten sam QueryPlanHash .
  2. Zwróć uwagę na górny diagram z połączeniem scalającym. Użyliśmy RIGHT OUTER JOIN, ale SQL Server zmienił go na LEFT OUTER JOIN. Zamienił również lewy i prawy stół. To sprawia, że ​​jest równy drugiemu zapytaniu z LEFT JOIN.

Jak widzisz teraz, wyniki są takie same. Wybierz więc, które z ZŁĄCZEŃ ZEWNĘTRZNYCH będzie wygodniejsze.

Dlaczego SQL Server zmienił RIGHT JOIN na LEFT JOIN?

Silnik bazy danych nie musi podążać za sposobem wyrażania sprzężeń logicznych. Dopóki może dawać prawidłowe wyniki w najszybszy sposób, jaki uważa za możliwy, wprowadzi zmiany. Nawet skróty.

Nie sądź, że RGHT JOIN jest zły, a LEFT JOIN jest dobry.

PRAWE ZŁĄCZENIE ZEWNĘTRZNE, PRZYKŁAD 2

Spójrz na poniższy przykład:

-- Get the unassigned addresses and the address types with no addresses
SELECT
 P.FirstName
,P.MiddleName
,P.LastName
,a.AddressLine1
,a.AddressLine2
,a.City
,adt.Name AS AddressType
FROM Person.Person p
RIGHT JOIN Person.BusinessEntityAddress bea ON P.BusinessEntityID = bea.BusinessEntityID
RIGHT JOIN Person.Address a ON bea.AddressID = a.AddressID
RIGHT JOIN person.AddressType adt ON bea.AddressTypeID = adt.AddressTypeID
WHERE P.BusinessEntityID IS NULL 

Z tego zapytania można uzyskać 2 rzeczy, jak widać na Rysunku 5 poniżej:

Wyniki zapytania przedstawiają następujące informacje:

  1. Nieprzypisane adresy – te rekordy to te z pustymi nazwami.
  2. Typy adresów bez adresów. Typy adresów Archiwum, Rozliczenia i Podstawowe nie mają odpowiednich adresów. Pochodzą one z rekordów od 817 do 819.

PEŁNE ZŁĄCZENIE ZEWNĘTRZNE (PEŁNE ZŁĄCZENIE)

FULL JOIN zwraca kombinację rzędów wewnętrznych i zewnętrznych, lewego i prawego.

-- Get people with and without addresses, unassigned addresses, and address types without addresses
SELECT
 P.FirstName
,P.MiddleName
,P.LastName
,a.AddressLine1
,a.AddressLine2
,a.City
,adt.Name AS AddressType
FROM Person.Person p
FULL JOIN Person.BusinessEntityAddress bea ON P.BusinessEntityID = bea.BusinessEntityID
FULL JOIN Person.Address a ON bea.AddressID = a.AddressID
FULL JOIN person.AddressType adt ON bea.AddressTypeID = adt.AddressTypeID

Zestaw wyników zawiera 20 815 rekordów. Jak można się spodziewać, jest to całkowita liczba rekordów z zestawu wyników INNER JOIN, LEFT JOIN i RIGHT JOIN.

LEFT i RIGHT JOIN zawierają klauzulę WHERE, aby wyświetlić tylko wyniki z wartościami null w lewej lub prawej tabeli.

ZŁĄCZENIE WEWNĘTRZNE POŁĄCZENIE LEWE
(GDZIE a.AddressID JEST NULL)
PRAWE POŁĄCZENIE
(GDZIE P.BusinessEntityID JEST NULL)
TOTAL (tak samo jak PEŁNE DOŁĄCZENIE)
18 798 rekordów 1198 rekordów 819 rekordów 20 815 rekordów

Zwróć uwagę, że FULL JOIN może generować ogromny zestaw wyników z dużych tabel. Dlatego używaj go tylko wtedy, gdy tylko tego potrzebujesz.

Praktyczne zastosowania OUTER JOIN

Jeśli nadal wahasz się, kiedy możesz i powinieneś użyć OUTER JOIN, oto kilka pomysłów.

Zewnętrzne połączenia, które wyprowadzają zarówno wewnętrzne, jak i zewnętrzne rzędy

Przykładami mogą być:

  • Alfabetyczna lista opłaconych i nieopłaconych zamówień klientów.
  • Alfabetyczna lista pracowników ze spóźnialstwem lub bez historii spóźnienia.
  • Lista ubezpieczających, którzy odnowili i nie odnowili swoich ostatnich polis ubezpieczeniowych.

Zewnętrzne połączenia, które wyprowadzają tylko zewnętrzne rzędy

Przykłady obejmują:

  • alfabetyczna lista pracowników bez wpisu do nagrody za spóźnienie
  • lista terytoriów bez klientów
  • lista agentów sprzedaży, którzy nie sprzedają określonego produktu
  • uzyskiwanie wyników z brakujących wartości, takich jak daty bez zamówień sprzedaży w danym okresie (przykład poniżej)
  • węzły bez dziecka w relacji rodzic-dziecko (przykład poniżej)

Uzyskiwanie wyników z brakujących wartości

Załóżmy, że musisz sporządzić raport. Raport ten musi pokazywać liczbę dni w każdym miesiącu w danym okresie, w których nie było żadnych zamówień. Nagłówek zamówienia sprzedaży w AdventureWorks zawiera daty zamówienia , ale nie mają dat bez zamówień. Co możesz zrobić?

1. Utwórz tabelę wszystkich dat w okresie

Przykładowy skrypt poniżej utworzy tabelę dat na cały rok 2014:

DECLARE @StartDate date = '20140101', @EndDate date = '20141231';

CREATE TABLE dbo.Dates
(
	d DATE NOT null PRIMARY KEY
)

WHILE @StartDate <= @EndDate
BEGIN
  INSERT Dates([d]) SELECT @StartDate;
  SET @StartDate = DATEADD(DAY, 1, @StartDate);
END

SELECT d FROM Dates ORDER BY [d];
2. Użyj LEFT JOIN, aby wyprowadzić dni bez zamówień
SELECT
 MONTH(d.d) AS [month]
,YEAR(d.d) AS [year]
,COUNT(*) AS NoOrderDays
FROM Dates d
LEFT JOIN Sales.SalesOrderHeader soh ON d.d = soh.OrderDate
WHERE soh.OrderDate IS NULL
GROUP BY YEAR(d.d), MONTH(d.d)
ORDER BY [year], [month]

Powyższy kod zlicza liczbę dni, w których nie zostały złożone żadne zamówienia. Nagłówek zamówienia sprzedaży zawiera daty z zamówieniami. Tak więc wartości null zwrócone w ramach przyłączenia będą liczone jako dni bez zamówień.

Tymczasem, jeśli chcesz poznać dokładne daty, możesz usunąć liczbę i grupowanie.

SELECT
 d.d
,soh.OrderDate
FROM Dates d
LEFT JOIN Sales.SalesOrderHeader soh ON d.d = soh.OrderDate
WHERE soh.OrderDate IS NULL

Lub, jeśli chcesz policzyć zamówienia w danym okresie i zobaczyć, która data ma zero zamówień, oto jak:

SELECT DISTINCT
 D.d AS SalesDate
,COUNT(soh.OrderDate) AS NoOfOrders
FROM Dates d
LEFT JOIN Sales.SalesOrderHeader soh ON d.d = soh.OrderDate
WHERE d.d BETWEEN '02/01/2014' AND '02/28/2014'
GROUP BY d.d
ORDER BY d.d

Powyższy kod zlicza zamówienia na luty 2014. Zobacz wynik:

Dlaczego podkreśla 3 lutego 2014 r.? W mojej kopii AdventureWorks nie ma zamówień sprzedaży na ten dzień.

Teraz zwróć uwagę na COUNT(soh.OrderDate) w kodzie. Później wyjaśnimy, dlaczego jest to tak ważne.

Uzyskiwanie węzłów bezdzietnych w relacjach rodzic-dziecko

Czasami musimy znać węzły bez dziecka w relacji rodzic-dziecko.

Wykorzystajmy bazę danych, której użyłem w moim artykule o HierarchyID. Musisz uzyskać węzły bez dzieci w tabeli relacji rodzic-dziecko za pomocą funkcji self-join.

SELECT 
 r1.RankParentId
,r1.Rank AS RankParent
,r.RankId
FROM Ranks r
RIGHT JOIN Ranks r1 ON r.RankParentId = r1.RankId
WHERE r.RankId is NULL 

Ostrzeżenia dotyczące korzystania z OUTER JOIN

Ponieważ OUTER JOIN może zwracać wewnętrzne wiersze, tak jak INNER JOIN, może być mylące. Problemy z wydajnością również mogą się wkradać. Zwróć więc uwagę na 3 punkty poniżej (od czasu do czasu do nich wracam – nie jestem młodszy, więc też zapominam).

Filtrowanie właściwej tabeli w LEFT JOIN z wartością inną niż Null w klauzuli WHERE

Może to stanowić problem, jeśli użyto LEFT OUTER JOIN, ale odfiltrowano prawą tabelę z wartością inną niż null w klauzuli WHERE. Powodem jest to, że stanie się funkcjonalnie odpowiednikiem INNER JOIN. Rozważ poniższy przykład:

USE AdventureWorks
GO

SELECT
 P.FirstName
,P.MiddleName
,P.LastName
,a.AddressLine1
,a.AddressLine2
,a.City
,adt.Name AS AddressType
FROM Person.Person p
LEFT JOIN Person.BusinessEntityAddress bea ON P.BusinessEntityID = bea.BusinessEntityID
LEFT JOIN Person.Address a ON bea.AddressID = a.AddressID
LEFT JOIN person.AddressType adt ON bea.AddressTypeID = adt.AddressTypeID
WHERE bea.AddressTypeID = 5 

Z powyższego kodu przyjrzyjmy się 2 tabelom:Osoba i Adres podmiotu gospodarczego . Osoba to lewa tabela, a BusinessEntityAddress to właściwy stół.

Używane jest LEFT JOIN, więc przyjmuje pusty BusinessEntityID gdzieś w BusinessEntityAddress . Tutaj zwróć uwagę na klauzulę WHERE. Filtruje odpowiednią tabelę za pomocą AddressTypeID =5. Całkowicie odrzuca wszystkie zewnętrzne wiersze w BusinessEntityAddress .

Może to być dowolne z poniższych:

  • Deweloper testuje coś w wyniku, ale zapomniał to usunąć.
  • INNER JOIN był zamierzony, ale z jakiegoś powodu użyto LEFT JOIN.
  • Deweloper nie rozumie różnicy między LEFT JOIN a INNER JOIN. Zakłada, że ​​którykolwiek z tych 2 zadziała i nie ma to znaczenia, ponieważ w tym przypadku wyniki są takie same.

Każdy z powyższych 3 jest zły, ale trzeci wpis ma inną implikację. Porównajmy powyższy kod z odpowiednikiem INNER JOIN:

SELECT
 P.FirstName
,P.MiddleName
,P.LastName
,a.AddressLine1
,a.AddressLine2
,a.City
,adt.Name AS AddressType
FROM Person.Person p
INNER JOIN Person.BusinessEntityAddress bea ON P.BusinessEntityID = bea.BusinessEntityID
INNER JOIN Person.Address a ON bea.AddressID = a.AddressID
INNER JOIN person.AddressType adt ON bea.AddressTypeID = adt.AddressTypeID
WHERE bea.AddressTypeID = 5

Wygląda podobnie do poprzedniego kodu, z wyjątkiem typu sprzężenia. Wynik również jest taki sam, ale należy zwrócić uwagę na logiczne odczyty w STATISTICS IO:

Na rysunku 7 pierwsze statystyki I/O pochodzą z użycia INNER JOIN. Całkowita liczba odczytów logicznych wynosi 177. Jednak drugie statystyki dotyczą LEFT JOIN z wyższą wartością odczytów logicznych wynoszącą 223. W związku z tym nieprawidłowe użycie LEFT JOIN w tym przykładzie będzie wymagało większej liczby stron lub zasobów z programu SQL Server. Dlatego będzie działać wolniej.

Na wynos

Jeśli zamierzasz wyprowadzić wewnętrzne wiersze, użyj INNER JOIN. W przeciwnym razie nie filtruj prawej tabeli w LEFT JOIN z wartością inną niż null. Jeśli tak się stanie, otrzymasz wolniejsze zapytanie niż w przypadku użycia INNER JOIN.

WSKAZÓWKA BONUSOWA :Ta sytuacja ma również miejsce w przypadku RIGHT JOIN, gdy lewa tabela jest filtrowana z wartością inną niż null.

Niewłaściwe użycie typów złączeń w łączeniu wielokrotnym

Załóżmy, że chcemy uzyskać wszystkie dostawców i liczbę zamówień zakupu produktów dla każdego. Oto kod:

USE AdventureWorks
GO

SELECT
 v.BusinessEntityID
,v.Name AS Vendor
,pod.ProductID
,pod.OrderQty
FROM Purchasing.Vendor v
LEFT JOIN Purchasing.PurchaseOrderHeader poh ON v.BusinessEntityID = poh.VendorID
LEFT JOIN Purchasing.PurchaseOrderDetail pod ON poh.PurchaseOrderID = pod.PurchaseOrderID 

Powyższy kod zwraca zarówno dostawców z zamówieniami zakupu, jak i tych bez. Rysunek 8 pokazuje rzeczywisty plan wykonania powyższego kodu.

Myśląc, że każde zamówienie zakupu ma gwarantowane szczegóły zamówienia, lepsze byłoby DOŁĄCZENIE WEWNĘTRZNE. Czy jednak tak jest naprawdę?

Najpierw zróbmy zmodyfikowany kod z INNER JOIN.

USE AdventureWorks
GO

SELECT
 v.BusinessEntityID
,v.Name AS Vendor
,pod.ProductID
,pod.OrderQty
FROM Purchasing.Vendor v
LEFT JOIN Purchasing.PurchaseOrderHeader poh ON v.BusinessEntityID = poh.VendorID
INNER JOIN Purchasing.PurchaseOrderDetail pod ON poh.PurchaseOrderID = pod.PurchaseOrderID 

Pamiętaj, że powyższe wymaganie mówi o „wszystkich” dostawcach. Ponieważ użyliśmy LEFT JOIN w poprzednim kodzie, otrzymamy dostawców bez zwracanych zamówień zakupu. Dzieje się tak z powodu pustego PurchaseOrderID .

Zmiana złączenia na INNER JOIN spowoduje odrzucenie wszystkich pustych PurchaseOrderID. Anuluje również wszystkie puste VendorID od Dostawcy stół. W efekcie staje się ZŁĄCZENIEM WEWNĘTRZNYM.

Czy to prawidłowe założenie? Plan wykonania ujawni odpowiedź:

Jak widać, wszystkie tabele zostały przetworzone za pomocą INNER JOIN. Dlatego nasze założenie jest prawidłowe. Ale co najgorsze, zestaw wyników jest teraz niepoprawny, ponieważ dostawcy bez zamówień nie zostali uwzględnieni.

Na wynos

Podobnie jak w poprzednim przypadku, jeśli zamierzasz INNER JOIN, użyj go. Ale wiesz, co zrobić, jeśli napotkasz sytuację taką jak ta tutaj.

W takim przypadku INNER JOIN odrzuci wszystkie zewnętrzne wiersze aż do górnej tabeli w relacji. Nawet jeśli twoje drugie sprzężenie to LEFT JOIN, nie ma to znaczenia. Udowodniliśmy to w Planach Wykonawczych.

Nieprawidłowe użycie COUNT() w połączeniach zewnętrznych

Pamiętasz nasz przykładowy kod, który zlicza liczbę zamówień na dzień i wynik na Rysunku 6?

Tutaj wyjaśnimy, dlaczego podświetlono 02/03/2014 i jego związek z COUNT(soh.OrderDate) .

Jeśli spróbujesz użyć COUNT(*), liczba zamówień na tę datę wynosi 1, co jest błędne. W tym dniu nie ma żadnych zamówień. Tak więc, używając funkcji COUNT() z OUTER JOIN, użyj właściwej kolumny do zliczania.

W naszym przypadku soh.OrderDate może być zerowa lub nie. Gdy nie jest null, COUNT() uwzględni wiersz w liczniku. COUNT(*) sprawi, że policzy wszystko, łącznie z wartościami null. I w końcu złe wyniki.

Na wynos OUTER JOIN

Podsumujmy punkty:

  • ZŁĄCZENIE ZEWNĘTRZNE może zwrócić zarówno wiersze wewnętrzne, jak i zewnętrzne. Wewnętrzne rzędy są wynikiem podobnym do wyniku INNER JOIN. Zewnętrzne wiersze to wartości inne niż null z ich odpowiednikami zerowymi opartymi na warunku złączenia.
  • ZŁĄCZENIE ZEWNĘTRZNE może być LEWE, PRAWE lub PEŁNE. Dla każdego mieliśmy przykłady.
  • Zewnętrzne wiersze zwracane przez OUTER JOIN można wykorzystać na wiele praktycznych sposobów. Mieliśmy pomysły, kiedy możesz użyć tych rzeczy.
  • Mieliśmy również pewne zastrzeżenia dotyczące korzystania z funkcji OUTER JOIN. Pamiętaj o 3 powyższych punktach, aby uniknąć błędów i problemów z wydajnością.

W końcowej części tej serii omówimy CROSS JOIN. Więc do tego czasu. A jeśli podoba Ci się ten post, podziel się miłością, klikając przyciski mediów społecznościowych. Miłego kodowania!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Przyszłość stosu aplikacji

  2. Usuwanie plików śledzenia za pomocą ADRCI

  3. Tworzenie bardziej zaawansowanego modelu ze statusami użytkownika, wątku i postu

  4. Jak obliczyć różnicę między dwiema datami w T-SQL

  5. Tworzenie modułu w języku Java 9 w środowisku Eclipse IDE, część 2