Vezérlőpult
7c. JOIN-ok — táblák összekapcsolása · Lekérdezések — SELECT

3. blokk · Lekérdezések — SELECT · 7c. témakör

JOIN-ok — táblák összekapcsolása

Egy webshop ereje a táblák kapcsolataiban van: ki rendelt mit, melyik kategóriában, mennyiért? A JOIN több táblát kapcsol össze egyetlen lekérdezésben. Ebben a részben halmaz-ábrákon (Venn-diagramokon) keresztül nézzük meg, melyik JOIN mit ad vissza — és pontosan hogyan párosulnak a sorok.

Élő SQL-gyakorlóVenn-diagramok10 kvízkérdés10 feladat

Tanulási célok

1Miért kell a JOIN?

A jól megtervezett adatbázisban az adatok külön táblákban élnek (a vásárló a vasarlo-ban, a rendelése a rendeles-ben). Ha egy kérdés több táblát is érint — pl. „melyik vásárló mikor rendelt?” —, akkor ezeket össze kell kapcsolnunk. Erre való a JOIN: a kapcsolódó táblák sorait egy közös érték mentén párosítja, és egyetlen, széles eredménytáblát ad.

ℹ️ EMLÉKEZTETŐ

A táblák az idegen kulcsokkal kapcsolódnak (lásd a tervezési és EER témaköröket): a rendeles.vasarlo_id a vasarlo.id-ra mutat. A JOIN éppen ezt a kapcsolatot használja fel a párosításhoz.

2Hogyan kapcsolódnak a táblák?

A JOIN-nak két dolgot kell megadnunk: melyik két táblát kapcsoljuk össze, és milyen feltétellel párosítjuk a sorokat. Ez utóbbi az ON feltétel — szinte mindig az idegen kulcs és a hivatkozott elsődleges kulcs egyenlősége:

SELECT v.vezeteknev, r.id, r.rendeles_datuma
FROM vasarlo v
JOIN rendeles r ON r.vasarlo_id = v.id;
--                  └── a kapcsolási feltétel: a gyermek FK = a szülő PK

A párosítás logikája: a szerver minden rendeles sorhoz megkeresi azt a vasarlo sort, amelyre az ON feltétel igaz (r.vasarlo_id = v.id), és a kettőt egyetlen sorrá fűzi össze. Az alábbi ábra ezt mutatja — a vonalak a párosított sorokat kötik össze:

ON r.vasarlo_id = v.id vasarlo id vezeteknev 1Nagy 2Kiss 3Tóth 4Szabó rendeles id vasarlo_id (FK) 101 111 122 13 NULL 3, 4: nincs rendelés
Nagy (1) két rendelése (10, 11), Kiss (2) egy rendelése (12). Tóth (3) és Szabó (4) egyik rendeléshez sem párosul — ők a LEFT JOIN-ban jelennek majd meg (NULL-lal). A 13-as egy „vendégrendelés”: nincs vásárlója — ő a RIGHT/FULL JOIN-nál kerül elő.

Az eredményt úgy képzeld el, mint egyetlen széles táblát, amelyben egymás mellett áll a két tábla összes oszlopa — minden sikeres párosítás egy-egy sor:

v.idv.vezeteknevr.idr.vasarlo_idr.rendeles_datuma
1Nagy1012026-03-01 10:00:00
1Nagy1112026-03-05 14:30:00
2Kiss1222026-03-07 09:15:00

Figyeld meg: Nagy kétszer szerepel, mert két rendelése van — egy bal oldali sor annyiszor jelenik meg, ahány pár jut rá. Erre a széles táblára működik aztán minden, amit eddig tanultunk (WHERE, ORDER BY, GROUP BY…), a SELECT-listával pedig csak a kívánt oszlopokat mutatjuk meg belőle.

✅ TÁBLA-ALIASOK — KÉNYELEM, NEM KÖTELEZŐ

Ha a két táblában azonos nevű oszlop van (pl. mindkettőben id), a nem egyértelmű oszlopok elé ki kell írni, melyik táblából jönnek — különben „ambiguous” hibát kapnánk. Erre két lehetőség van: a teljes táblanév vagy egy rövid alias — mindkettő helyes:

-- 1. Teljes táblanevekkel — alias nélkül is működik:
SELECT vasarlo.vezeteknev, rendeles.id, rendeles.rendeles_datuma
FROM vasarlo
JOIN rendeles ON rendeles.vasarlo_id = vasarlo.id;

-- 2. Rövid aliasokkal — ugyanaz, csak tömörebb (a gyakorlatban ezt szokás):
SELECT v.vezeteknev, r.id, r.rendeles_datuma
FROM vasarlo v
JOIN rendeles r ON r.vasarlo_id = v.id;

Emlékezz a 7a-ból: ha aliast adtál, a tábla az eredeti nevén abban a lekérdezésben már nem érhető el. Egyetlen hely van, ahol az alias tényleg kötelező: az önkapcsolás (9. szakasz), mert ott ugyanaz a tábla kétszer szerepel.

3INNER JOIN — csak az egyezők

Az INNER JOIN (vagy egyszerűen JOIN) csak azokat a sorokat adja vissza, amelyekhez mindkét táblában van pár. Halmazként ez a két tábla metszete.

A tábla B tábla 1 2 3 4

Eredmény: { 2, 3 }

A = {1, 2, 3}   B = {2, 3, 4}

Csak a közös kulcsok (2 és 3) maradnak. Az 1 (csak A-ban) és a 4 (csak B-ben) kimarad.

Webshop: csak azok a vásárlók, akiknek van rendelése.

SELECT v.vezeteknev, r.id AS rendeles_id, r.rendeles_datuma
FROM vasarlo v
INNER JOIN rendeles r ON r.vasarlo_id = v.id;
ℹ️ AZ „INNER” ELHAGYHATÓ

Ha csak annyit írsz, hogy JOIN, az alapból INNER JOIN-t jelent. A két forma teljesen egyenértékű.

4LEFT JOIN — a bal tábla minden sora

A LEFT JOIN (más néven LEFT OUTER JOIN) a bal tábla összes sorát visszaadja, és mellé a jobb tábla egyező sorait. Ahol a bal sorhoz nincs pár a jobb táblában, ott a jobb oldali oszlopok NULL-ok lesznek.

A (bal) B (jobb) 1 2 3 4

Eredmény: { 1, 2, 3 }

A = {1, 2, 3}   B = {2, 3, 4}

A bal tábla minden sora megjelenik. Az 1-hez nincs B-beli pár → a B-oszlopok NULL-ok. A 4 (csak B-ben) kimarad.

Webshop: minden vásárló, a rendelés nélkülieknél NULL.

-- MINDEN vásárló, a rendeléseivel (vagy nélkülük):
SELECT v.vezeteknev, r.id AS rendeles_id
FROM vasarlo v
LEFT JOIN rendeles r ON r.vasarlo_id = v.id;
vezeteknevrendeles_id
Nagy10
Nagy11
Kiss12
TóthNULL
SzabóNULL

Tóth és Szabó is megjelenik — a hiányzó pár helyén NULL-lal. (A vásárló nélküli 13-as rendelés viszont itt nem szerepel: az a jobb tábla párja nélküli sora.)

✅ MIRE JÓ?

A LEFT JOIN tipikus haszna: „kik nem rendeltek még?” Ehhez LEFT JOIN után WHERE r.id IS NULL szűréssel pont a pár nélküli bal sorokat kapod meg.

6FULL OUTER JOIN — minden sor mindkét oldalról

A FULL OUTER JOIN mindkét tábla összes sorát visszaadja: ahol van pár, összefűzi; ahol nincs (akár bal, akár jobb oldalon), ott a hiányzó oldal oszlopai NULL-ok. Halmazként ez a két tábla uniója.

A tábla B tábla 1 2 3 4

Eredmény: { 1, 2, 3, 4 }

A = {1, 2, 3}   B = {2, 3, 4}

Minden kulcs megjelenik: az 1-nél a B-oldal, a 4-nél az A-oldal NULL. A 2, 3 párba állítva.

⚠️ MYSQL: NINCS KÖZVETLEN FULL JOIN

A MySQL nem támogatja közvetlenül a FULL OUTER JOIN-t! Helyette egy LEFT JOIN és egy RIGHT JOIN eredményét egyesítjük UNION-nal. (Az alábbi gyakorló SQLite-motorja viszont ismeri a FULL JOIN-t, így ott közvetlenül is kipróbálható.)

7CROSS JOIN — Descartes-szorzat

A CROSS JOIN-nak nincs ON feltétele: a bal tábla minden sorát összepárosítja a jobb tábla minden sorával. Ez a halmazok Descartes-szorzata. Az eredmény sorszáma a két tábla sorszámának szorzata.

A = {1, 2} B = {a, b} A × B (2 × 2 = 4 sor): (1, a) (1, b) (2, a) (2, b)
2 elem × 2 elem = 4 kombináció. 100 termék × 50 vásárló = 5000 sor!
-- Minden vásárló minden kategóriával párosítva (4 × 3 = 12 sor):
SELECT v.vezeteknev, k.nev AS kategoria
FROM vasarlo v
CROSS JOIN kategoria k
ORDER BY v.vezeteknev, kategoria;

Mikor jó ez nekünk? Amikor tényleg minden kombinációra szükség van:

⚠️ VIGYÁZAT

A CROSS JOIN könnyen hatalmas eredményt ad (a sorszámok szorzata). Ritkán használjuk szándékosan — gyakran úgy keletkezik véletlenül, hogy elfelejtjük az ON feltételt egy sima JOIN-nál!

8Több tábla összekapcsolása

Több JOIN is láncolható egymás után. A webshop „teljes” kérdéséhez — „ki, mit, mennyiért rendelt?” — négy táblát kötünk össze a kapcsolataik mentén: vasarlo → rendeles → rendeles_tetel → termek.

SELECT v.vezeteknev,
       r.id AS rendeles,
       t.nev AS termek,
       rt.mennyiseg,
       rt.egysegar
FROM vasarlo v
JOIN rendeles r        ON r.vasarlo_id = v.id
JOIN rendeles_tetel rt ON rt.rendeles_id = r.id
JOIN termek t          ON t.id = rt.termek_id;

Így oldjuk fel az N:M kapcsolatot is: a rendeles_tetel kapcsolótáblán keresztül kötjük össze a rendeléseket a termékekkel. Aggregálással pedig pl. vásárlónkénti összköltést kapunk:

SELECT v.vezeteknev, SUM(rt.mennyiseg * rt.egysegar) AS osszkoltes
FROM vasarlo v
JOIN rendeles r        ON r.vasarlo_id = v.id
JOIN rendeles_tetel rt ON rt.rendeles_id = r.id
GROUP BY v.id
ORDER BY osszkoltes DESC;

9Önkapcsolás (self join)

Egy tábla önmagával is összekapcsolható — ez az önkapcsolás. Akkor kell, ha egy soron belül egy másik ugyanabban a táblában lévő sorra hivatkozunk. Webshop-példa: a kategoria tábla szulo_id oszlopa egy másik kategóriára mutat. A szülő nevének megjelenítéséhez a táblát kétszer használjuk, két különböző aliasszal:

SELECT gy.nev AS alkategoria,
       sz.nev AS szulo_kategoria
FROM kategoria gy
LEFT JOIN kategoria sz ON gy.szulo_id = sz.id;
-- gy = gyermek, sz = szülő — ugyanaz a tábla, két szerepben
✅ MIÉRT LEFT JOIN?

A főkategóriáknak (pl. „Elektronika”) nincs szülőjük (szulo_id IS NULL). A LEFT JOIN így is megjeleníti őket — a szulo_kategoria oszlopukban NULL-lal. Sima JOIN-nal kimaradnának.

10Élő SQL-gyakorló

Próbáld ki a JOIN-okat a webshopon! Figyeld meg, hogyan jelennek meg / tűnnek el a rendelés nélküli vásárlók (Tóth, Szabó) és a vásárló nélküli 13-as „vendégrendelés” a különböző JOIN-típusoknál.

SQL-gyakorló — JOIN-ok

SQL-motor betöltése…
⚠️ A GYAKORLÓRÓL

A motor SQLite-alapú, amely az INNER, LEFT, RIGHT, FULL OUTER és CROSS JOIN-t is ismeri. Fontos: a MySQL viszont a RIGHT-ot ismeri, de a FULL OUTER JOIN-t nem — ott UNION-nal helyettesítjük. (Internetkapcsolat szükséges a motorhoz.)

11Feladatok

Gyakorló feladatok

⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a kódírós feladatokat futtasd is le a fenti élő gyakorlóban!

7c.1⭐

Mivel adjuk meg a JOIN kapcsolási feltételét?

7c.2⭐

Miért használunk tábla-aliasokat a JOIN-okban? Adj legalább egy okot!

7c.3⭐⭐

Hány táblát és milyen sorrendben kapcsolnál össze, hogy lásd, „ki, milyen terméket rendelt”?

7c.4⭐⭐

Mit ad vissza ez a lekérdezés? (Tóth és Szabó nem rendelt.) Előbb gondold végig, aztán futtasd le!

SELECT v.vezeteknev, r.id
FROM vasarlo v
LEFT JOIN rendeles r ON r.vasarlo_id = v.id;
7c.5⭐⭐⭐

Miért lehet veszélyes ez a lekérdezés?

SELECT v.vezeteknev, t.nev
FROM vasarlo v
JOIN termek t;
7c.6⭐⭐⭐

Mit csinál a gy és sz alias ebben a lekérdezésben?

SELECT gy.nev AS alkategoria, sz.nev AS szulo_kategoria
FROM kategoria gy
LEFT JOIN kategoria sz ON gy.szulo_id = sz.id;
7c.7⭐

Kódírás: írj lekérdezést, amely kiírja minden rendelés azonosítóját és a hozzá tartozó vásárló vezetéknevét (csak a tényleges rendeléseket)!

7c.8⭐⭐

Kódírás: listázd azokat a vásárlókat (vezetéknév), akiknek nincs egyetlen rendelésük sem!

7c.9⭐⭐⭐

Kódírás: add meg vásárlónként az összes elköltött összeget (mennyiség × egységár), a legtöbbet költőtől kezdve!

7c.10⭐⭐

Kódírás: írd ki minden termék nevét a kategóriája nevével együtt — azokat is, amelyeknek nincs kategóriája!

12Önellenőrző kvíz

Ellenőrizd magad!

Minden kérdésnél egy válasz helyes. A végén pontszámot kapsz.

13Fogalomtár

FogalomJelentés
JOIN · ONtáblák sorainak párosítása az ON feltétel (FK = PK) mentén
tábla-aliasrövid név a táblának (vasarlo v) — nem kötelező: a nem egyértelmű oszlop a teljes táblanévvel is minősíthető; önkapcsolásnál viszont muszáj
INNER JOINcsak az egyező sorok — a két tábla metszete
LEFT JOINa bal tábla minden sora; pár híján a jobb oldal NULL
RIGHT JOINa jobb tábla minden sora — a LEFT tükörképe
FULL OUTER JOINmindkét tábla minden sora (unió) — MySQL-ben UNION-nal
CROSS JOINDescartes-szorzat ON nélkül — a sorszámok szorzata
„kik nem” mintaLEFT JOIN + WHERE jobb.id IS NULL — a pár nélküli bal sorok
láncolt JOINtöbb tábla sorban összekapcsolva (N:M a kapcsolótáblán át)
önkapcsolás (self join)a tábla önmagával, két aliasszal (pl. gyermek–szülő kategória)

14Összegzés

  1. A JOIN a táblák sorait egy ON feltétel (jellemzően FK = PK) mentén párosítja.
  2. INNER JOIN: csak a két tábla metszete (az egyező sorok).
  3. LEFT JOIN: a bal tábla minden sora; pár hiányában a jobb oldal NULL.
  4. RIGHT JOIN: a jobb tábla minden sora (a LEFT tükörképe).
  5. FULL OUTER JOIN: mindkét tábla minden sora (MySQL-ben UNION-nal).
  6. CROSS JOIN: Descartes-szorzat, ON nélkül — a sorszámok szorzata.
  7. Több tábla láncolható; az önkapcsolás a táblát önmagával köti össze (két alias).