Jak połączyć dwie tabele w jednym zapytaniu po kluczu obcym, czym różni się INNER JOIN od LEFT JOIN, co daje iloczyn kartezjański i jak czytać wynik na egzaminie. Lekcja 8.6 kursu INF.03.
Złączenie (JOIN, czyli POŁĄCZ) to połączenie wierszy z dwóch tabel po wspólnej wartości, prawie zawsze po kluczu obcym. Na egzaminie pisemnym pytanie pokazuje dwie tabele i wynik, a trzeba wskazać zapytanie, które ten wynik dało. W zadaniu praktycznym jedna z czterech kwerend „wykorzystuje relację między tabelami", czyli wymaga złączenia. Po tej lekcji piszesz je bez zaglądania do ściągi.
Dwie tabele i relacja
Baza sklepu z lekcji o SELECT dostaje dwie tabele więcej: klientów i ich zamówienia.
[ TABELE KLIENT I ZAMOWIENIE ]
Tabela klient:
id
imie
miasto
1
Anna
Kraków
2
Piotr
Gdańsk
3
Ewa
Kraków
Tabela zamowienie:
id
klient_id
produkt_id
ilosc
10
1
3
1
11
1
1
2
12
2
5
3
13
NULL
2
1
Kolumna klient_id w tabeli zamowienie to klucz obcy: wskazuje na id w tabeli klient. Anna (id 1) ma dwa zamówienia, Piotr jedno, Ewa żadnego. Zamówienie 13 nie ma klienta, na przykład był to zakup bez konta. Ta jedna relacja „jeden klient, wiele zamówień" wystarczy, żeby zrozumieć wszystkie rodzaje złączeń.
Po co łączyć
Pytanie „kto zamówił monitor" wymaga danych z obu tabel: imię jest w klient, numer produktu w zamowienie. Bez złączenia trzeba by dwóch zapytań i ręcznego dopasowywania. Złączenie robi to w jednym:
SELECT klient.imie, zamowienie.produkt_id
FROM klient
INNER JOIN zamowienie ON zamowienie.klient_id = klient.id;
Czytaj: weź tabelę klient, dołącz do niej zamowienie, a parą są te wiersze, w których klient_id zamówienia równa się id klienta. ON (NA) mówi, na jakiej podstawie łączyć. Nazwa tabeli przed kropką mówi, z której tabeli jest kolumna. Gdy nazwa kolumny nie powtarza się w drugiej tabeli, przedrostek można pominąć, ale id jest w obu, więc klient.id trzeba zapisać w całości.
Wynik:
imie
produkt_id
Anna
3
Anna
1
Piotr
5
Anna jest dwa razy, bo ma dwa zamówienia. Ewy nie ma wcale. Zamówienia 13 też nie ma. To jest właśnie cecha złączenia wewnętrznego.
INNER JOIN: tylko dopasowane pary
INNER JOIN (ZŁĄCZENIE WEWNĘTRZNE) zwraca wyłącznie wiersze, które mają parę po obu stronach. Klient bez zamówień znika, zamówienie bez klienta znika. Samo słowo JOIN bez przymiotnika znaczy w MySQL to samo, co INNER JOIN.
Trzy diagramy kół: INNER JOIN to część wspólna, LEFT JOIN to całe lewe koło, RIGHT JOIN to całe prawe koło
LEFT JOIN: wszystko z lewej
LEFT JOIN (ZŁĄCZENIE LEWE) zwraca wszystkie wiersze tabeli stojącej po lewej stronie słowa JOIN, a z prawej dopasowane wiersze albo NULL, gdy pary nie ma.
SELECT klient.imie, zamowienie.id
FROM klient
LEFT JOIN zamowienie ON zamowienie.klient_id = klient.id;
imie
id
Anna
10
Anna
11
Piotr
12
Ewa
NULL
Ewa jest w wyniku, z NULL zamiast numeru zamówienia. Zamówienia 13 nadal nie ma, bo lewą tabelą jest klient, a nie zamowienie. Ten wynik odpowiada na pytanie „którzy klienci nic nie zamówili": wystarczy dopisać WHERE zamowienie.id IS NULL.
Zamień LEFT na INNER i porównaj liczbę wierszy:
RIGHT JOIN (ZŁĄCZENIE PRAWE) działa lustrzanie: wszystko z prawej tabeli, z lewej para albo NULL. W tym samym zapytaniu z RIGHT JOIN pojawiłoby się zamówienie 13 z NULL w kolumnie imie, a Ewy by nie było. W praktyce RIGHT JOIN pisze się rzadko, bo zamiana kolejności tabel i LEFT JOIN dają to samo.
Pułapka: o tym, która tabela jest „lewa", decyduje miejsce w zapytaniu, nie kolejność w warunku ON. Lewa to ta po FROM, prawa to ta po JOIN.
Złączenie bez warunku: iloczyn kartezjański
Kiedy zapytanie łączy tabele bez warunku, baza paruje KAŻDY wiersz pierwszej tabeli z KAŻDYM wierszem drugiej. To iloczyn kartezjański, w SQL także CROSS JOIN (ZŁĄCZENIE KRZYŻOWE).
SELECT klient.imie, zamowienie.id FROM klient, zamowienie;
Trzech klientów razy cztery zamówienia daje dwanaście wierszy, w większości bez sensu: Anna z zamówieniem Piotra, Ewa z każdym zamówieniem po kolei. Pytania egzaminacyjne lubią to jako rachunek: tabela z 5 wierszami i tabela z 8 wierszami złączone bez warunku dają 40 wierszy.
Ten sam przecinkowy zapis z warunkiem w WHERE daje wynik złączenia wewnętrznego:
SELECT klient.imie, zamowienie.id
FROM klient, zamowienie
WHERE zamowienie.klient_id = klient.id;
Starsze pytania i część arkuszy używają tej formy. Wynik jest identyczny z INNER JOIN ... ON, więc obie postacie trzeba rozpoznawać jako tę samą operację.
Aliasy i trzy tabele
AS skraca nazwy tabel, co przy trzech tabelach ratuje czytelność. Samo słowo AS można pominąć i napisać zamowienie z.
SELECT k.imie, p.nazwa, z.ilosc
FROM zamowienie AS z
INNER JOIN klient AS k ON z.klient_id = k.id
INNER JOIN produkt AS p ON z.produkt_id = p.id;
Każde kolejne JOIN dokłada jedną tabelę i jeden warunek ON. Wynik: Anna, Monitor 24, 1; Anna, Klawiatura, 2; Piotr, Kabel HDMI, 3. Tak wygląda typowa kwerenda „z wykorzystaniem relacji" w zadaniu praktycznym: tabela pośrednia (zamowienie) w środku, dwie tabele słownikowe po bokach.
UNION: wyniki jeden pod drugim
UNION (SUMA) to nie złączenie, ale bywa z nim mylony. Skleja wyniki dwóch zapytań w jedną listę wierszy, jeden pod drugim, i usuwa powtórzenia (UNION ALL je zostawia). Oba zapytania muszą zwracać tyle samo kolumn tego samego typu.
SELECT miasto FROM klient
UNION
SELECT miasto FROM dostawca;
JOIN dokłada KOLUMNY z drugiej tabeli obok. UNION dokłada WIERSZE pod spodem.
Na egzaminie
Pytanie pisemne pokazuje dwie tabele po trzy, cztery wiersze i tabelę wynikową. Metoda: policz wiersze wyniku i sprawdź, czy są w nim NULL. Tyle wierszy, ile dopasowanych par, i bez NULL: INNER JOIN. Wszystkie wiersze lewej tabeli i NULL po prawej: LEFT JOIN. Liczba wierszy równa iloczynowi liczb wierszy: brak warunku. Druga rzecz do sprawdzenia to sam warunek ON: klucz obcy jednej tabeli musi być porównany z kluczem głównym drugiej. ON klient.id = zamowienie.id porównuje dwa niezwiązane numery i jest błędną odpowiedzią, która na pierwszy rzut oka wygląda poprawnie.
W zadaniu praktycznym arkusz opisuje kwerendę słowami, na przykład „wypisuje imiona klientów i nazwy zamówionych produktów". Każda wymieniona kolumna wskazuje tabelę, a droga między tabelami idzie po kluczach obcych ze schematu podanego w arkuszu. Kwerendę zapisz w kwerendy.txt dokładnie w tej postaci, którą wykonałeś, ze średnikiem na końcu.
Ściąga
Zapis
Co zwraca
A INNER JOIN B ON warunek
tylko pary spełniające warunek
A JOIN B ON warunek
to samo, co INNER JOIN
A LEFT JOIN B ON warunek
wszystkie wiersze A, z B para albo NULL
A RIGHT JOIN B ON warunek
wszystkie wiersze B, z A para albo NULL
FROM A, B bez warunku
iloczyn kartezjański: każdy z każdym
FROM A, B WHERE a.x = b.y
jak INNER JOIN
tabela.kolumna
kolumna z konkretnej tabeli; konieczne, gdy nazwa się powtarza
tabela AS t
alias, krótsza nazwa tabeli w zapytaniu
SELECT ... UNION SELECT ...
wiersze dwóch zapytań jeden pod drugim, bez powtórzeń