Dziś wpis na FULL! Jak iść, to na całość!
Na Twoim ulubionym blogu pojawiały się już wpisy związane ze złączeniem LEFT JOIN, być może słyszałeś o złączeniu RIGHT JOIN (to jakby odbicie lustrzane LEFT JOIN), ale nie pojawiały się o FULL JOIN.
Skorzystamy z naszego oklepanego przykładu, czyli tabeli klient oraz tabeli zamowienie. W takim przypadku złączenie FULL JOIN wybierze nam klientów bez zamówień, klientów z zamówieniami (lub zamówienia klientów), oraz zamówienia bez klientów. Praktycznym przykładem może być po prostu widok gdzie chcemy zobaczyć listę klientów i ich ewentualne zamówienia oraz zamówienia bez przypisanych klientów. Pewnie zadajesz sobie pytanie skąd zamówienia bez klientów, ale możemy w tym momencie założyć, że ze względu na RODO (to już nie jest typ złączenia...) usunąłeś klientów, ale pozostawiłeś ich zamówienia. Co prawda nie dowiesz się kto dokładnie złożył takiego typu zamówienie, ale będziesz miec zachowaną historię sprzedaży.
Zbudujmy więc zapytanie oparte na tym złączeniu:
SELECT k.id_klient,z.id_zamowienie
FROM klient AS k
FULL OUTER JOIN zamowienie AS z ON z.id_klient=k.id_klient
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FULL OUTER JOIN...
Czyli tyle czytania na marne?
Jak w takim razie wykonać takie złączenie w MySQL?
Może przemknęło Ci przez myśl, czytając zasadę działania tego złączenia, że łączy (sic!) w sobie cechy LEFT JOIN i RIGHT JOIN. Czyli dwa złączenia w jednym. Wykorzystajmy więc ten pomysł i zbudujmy takie zapytanie:
Tabela klient:
id_klient | login |
---|---|
1 | marek |
2 | zdzisiek |
3 | heniek |
Tabela zamowienie:
id_zamowienie | id_klient | suma |
---|---|---|
1 | 1 | 10 |
2 | 1 | 15 |
3 | 1 | 9 |
4 | 3 | 100 |
5 | null | 50 |
6 | null | 75 |
SELECT k.id_klient,z.id_zamowienie
FROM klient AS k
LEFT JOIN zamowienie AS z ON z.id_klient=k.id_klient
UNION
SELECT k.id_klient,z.id_zamowienie
FROM klient AS k
RIGHT JOIN zamowienie AS z ON z.id_klient=k.id_klient
Otrzymany wynik:
id_klient | id_zamowienie |
---|---|
1 | 1 |
1 | 2 |
1 | 3 |
3 | 4 |
2 | null |
null | 5 |
null | 6 |
Widzimy tu identyfikator klienta zdzisiek (id_klient=2), który nadal nie złożył zamówienia oraz dwa zamówienia nieprzypisane do klientów (id_zamowienie=5,6).
I w tym momencie mógłbym zakończyć ten wpis, bo wynik jest oczekiwany - taki jak sobie założyliśmy. To prawda, jest, ale nie jest poprawny, bowiem FULL OUTER JOIN nie eliminuje duplikatów łącząc obydwie tabele. Już wyjaśniam.
Po prostu ten typ złączenia odrębnie traktuje wynik złączenia tabeli klient z tabelą zamowienie, oraz wynik łączenia tabeli zamowienie z tabelą klient. Czyli jest to dokładnie suma złączeń LEFT JOIN oraz RIGHT JOIN bez eliminacji duplikatów powstających w wyniku tychże złączeń.
Taki jest poprawny wynik:
id_klient | id_zamowienie |
---|---|
1 | 1 |
1 | 2 |
1 | 3 |
3 | 4 |
2 | null |
1 | 1 |
1 | 2 |
1 | 3 |
3 | 4 |
null | 5 |
null | 6 |
A poprawne zapytanie zawiera po prostu operator UNION ALL zamiast UNION:
SELECT k.id_klient,z.id_zamowienie
FROM klient AS k
LEFT JOIN zamowienie AS z ON z.id_klient=k.id_klient
UNION ALL
SELECT k.id_klient,z.id_zamowienie
FROM klient AS k
RIGHT JOIN zamowienie AS z ON z.id_klient=k.id_klient
Jeszcze tylko mały tip na zakończenie (głupio by było kończyć tak banalnie). Skoro RIGHT JOIN to lustrzane odbicie LEFT JOIN, w takim razie możemy wykorzystać w obydwu przypadkach LEFT JOIN - pamiętajmy o "odwróceniu" łączonych tabel.
SELECT t1.id_klient,t2.id_zamowienie
FROM klient AS t1
LEFT JOIN zamowienie AS t2 ON t2.id_klient=t1.id_klient
UNION /* Unikamy jednak duplikatów */
SELECT t2.id_klient,t1.id_zamowienie
FROM zamowienie AS t1
LEFT JOIN klient AS t2 ON t2.id_klient=t1.id_klient
To tyle na dziś. Full night. Przepraszam good night.
Przydatne linki:
Złączenie FULL OUTER JOIN