Vezérlőpult
7d. Beágyazott lekérdezések · Lekérdezések — SELECT

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

Beágyazott lekérdezések

Néha egy lekérdezéshez előbb egy másik lekérdezés eredménye kell: „melyik termék drágább az átlagnál?”, „kik nem rendeltek?”. A beágyazott lekérdezés (subquery) egy lekérdezés egy másik lekérdezésen belül. Megnézzük, hol állhat, milyen formákban, és milyen kötöttségekkel.

Élő SQL-gyakorló11 kvízkérdés9 feladatWebshop-séma

Tanulási célok

1Mi a beágyazott lekérdezés?

A beágyazott lekérdezés (angolul subquery) egy zárójelbe tett SELECT, amely egy nagyobb (külső) lekérdezés része. A szerver előbb a belső lekérdezést futtatja le, és annak eredményét használja fel a külső lekérdezésben.

-- "Mely termékek drágábbak az átlagnál?"
SELECT nev, ar
FROM termek
WHERE ar > (SELECT AVG(ar) FROM termek);
--        └── belső lekérdezés: kiszámolja az átlagot (pl. 87990)

A belső lekérdezés itt egyetlen számot ad (az átlagárat), a külső pedig ezzel hasonlít össze. Sokszor JOIN-nal is megoldható ugyanaz — a subquery viszont gyakran olvashatóbb, ha „egy érték kell egy másik lekérdezésből” a feladat.

2Hol állhat beágyazott lekérdezés?

Subquery több fő helyen állhat egy SELECT-ben. Az alábbi vázlaton a számok jelölik ezeket a helyeket:

SELECT oszlopok, () AS ertek
FROM   () AS sub
WHERE  oszlop = ();

👆 Kattints a számokra — megmutatjuk, mi állhat az adott helyen!

Mindegyik helynek megvan a maga tipikus formája és kötöttsége:

HelyMire jóMit adhat vissza
WHERE / HAVINGSzűrés egy másik lekérdezés eredménye alapjánSkalár (1 érték) összehasonlításhoz; vagy 1 oszlop több sora az IN-hez
SELECT-listaEgy számolt érték oszlopként minden sorhozPontosan egy érték (skalár), soronként
FROMEgy „ideiglenes tábla”, amin tovább dolgozunkTeljes táblát (több sor, több oszlop) — kötelező alias
ℹ️ A FŐ SZEMPONT

Mindig kérdezd meg: a subquery hány értéket ad vissza? Egyet (skalár) → összehasonlításhoz (=, >) vagy a SELECT-listába. Egy oszlop több sorát → IN-hez. Egész táblát → a FROM-ba. Ez dönti el, hová illeszthető.

3Skalár subquery a WHERE-ben

A skalár subquery pontosan egy sort és egy oszlopot (tehát egyetlen értéket) ad vissza. Ezzel összehasonlító operátort (=, >, <…) használhatunk:

-- A legdrágább termék(ek):
SELECT nev, ar FROM termek
WHERE ar = (SELECT MAX(ar) FROM termek);
⚠️ KÖTÖTTSÉG: PONTOSAN EGY ÉRTÉK

Ha skalár helyen (pl. ar = (...)) a subquery több sort ad vissza, az hibát okoz („Subquery returns more than 1 row”). Ilyenkor vagy szűkítsd egy sorra (pl. MAX, LIMIT 1), vagy használj IN-t a = helyett.

4IN / NOT IN — egy oszlop több sora

Az IN egy oszlopnyi értékhalmazzal hasonlít: a sor akkor felel meg, ha az értéke szerepel a subquery által visszaadott listában.

-- Azok a termékek, amelyek az "Elektronika" alkategóriáiban vannak:
SELECT nev FROM termek
WHERE kategoria_id IN (SELECT id FROM kategoria WHERE szulo_id = 1);
⚠️ NOT IN + NULL = CSAPDA

Ha a subquery akár egyetlen NULL-t is visszaad, a NOT IN az egész feltételt „ismeretlenné” teszi, és nulla sort ad — látszólag indok nélkül! Ezért a NOT IN subquerynél biztosítsd, hogy ne legyen NULL az eredményben (pl. WHERE oszlop IS NOT NULL), vagy használj NOT EXISTS-t helyette.

5EXISTS / NOT EXISTS — létezik-e ilyen sor?

Az EXISTS nem értéket hasonlít, hanem azt nézi, hogy a belső lekérdezés ad-e legalább egy sort. Igaz/hamis eredményt ad. Tipikusan korrelált (a külső sorra hivatkozik):

-- Azok a vásárlók, akiknek VAN legalább egy rendelése:
SELECT v.vezeteknev FROM vasarlo v
WHERE EXISTS (SELECT 1 FROM rendeles r WHERE r.vasarlo_id = v.id);

-- Akiknek NINCS rendelése (a NOT IN biztonságos alternatívája):
SELECT v.vezeteknev FROM vasarlo v
WHERE NOT EXISTS (SELECT 1 FROM rendeles r WHERE r.vasarlo_id = v.id);
✅ A SELECT 1 TRÜKK

Az EXISTS-nél mindegy, mit ír a belső SELECT (gyakran SELECT 1) — csak a sorok létezése számít, az értékek nem. Az EXISTS sokszor gyorsabb is, mert leáll az első találatnál.

6Korrelált vs független subquery

Független (nem korrelált)Korrelált
Hivatkozik a külső sorra?NemIgen
Hányszor fut le?Egyszer (az érték újrahasználható)Külső soronként egyszer (logikailag)
Példaar > (SELECT AVG(ar) FROM termek)EXISTS (SELECT 1 FROM rendeles r WHERE r.vasarlo_id = v.id)

A független subquery önállóan is lefutna (nem ismeri a külső lekérdezést). A korrelált a külső lekérdezés aktuális sorára hivatkozik, ezért „minden külső sorhoz” kiértékelődik — ez erősebb, de nagy táblán lassabb lehet.

7Subquery a SELECT-listában és a FROM-ban

A SELECT-listában (skalár oszlop)

Egy skalár subquery oszlopként is állhat — minden külső sorhoz kiszámol egy értéket. Pl. vásárlónként a rendelések száma, JOIN nélkül:

SELECT v.vezeteknev,
       (SELECT COUNT(*) FROM rendeles r WHERE r.vasarlo_id = v.id) AS rendelesek
FROM vasarlo v;

A FROM-ban (származtatott tábla)

A FROM-ban álló subquery egy ideiglenes táblát (derived table) hoz létre, amin a külső lekérdezés tovább dolgozhat — pl. egy aggregált eredményt szűrünk tovább:

-- Csak azok a kategóriák, ahol az átlagár 10 000 Ft felett van:
SELECT kat, atlagar
FROM (SELECT kategoria_id AS kat, AVG(ar) AS atlagar
      FROM termek GROUP BY kategoria_id) AS sub
WHERE atlagar > 10000;
⚠️ KÖTÖTTSÉG: KÖTELEZŐ ALIAS

A FROM-ban álló subquerynek mindig kell adni egy aliast (itt sub), különben hibát kapsz. Ez a derived table neve, amire a külső lekérdezésben hivatkozhatsz.

8ANY / ALL és a kötöttségek összefoglalója

A MySQL ismeri az ANY és ALL operátorokat is, amelyek egy összehasonlítást egy egész oszlopnyi értékre alkalmaznak:

-- ANY: igaz, ha LEGALÁBB EGY értéknél teljesül (= IN rokona)
SELECT nev FROM termek
WHERE ar > ANY (SELECT ar FROM termek WHERE kategoria_id = 3);
-- (drágább, mint a 3-as kategória LEGOLCSÓBB terméke)

-- ALL: igaz, ha MINDEN értéknél teljesül
SELECT nev FROM termek
WHERE ar > ALL (SELECT ar FROM termek WHERE kategoria_id = 3);
-- (drágább, mint a 3-as kategória LEGDRÁGÁBB terméke)

Ezek gyakran kiválthatók MAX/MIN-nel: > ALL (...) = > (SELECT MAX(...)), és > ANY (...) = > (SELECT MIN(...)).

A kötöttségek egy helyen

KontextusA subquery mit adhatKötöttség / buktató
Összehasonlítás (=, >)Pontosan 1 érték (skalár)Több sor → hiba
IN / NOT IN1 oszlop, több sorNOT IN + NULL → 0 sor (csapda)
EXISTSBármit (csak létezést néz)Általában korrelált; SELECT 1 elég
SELECT-listaPontosan 1 érték, soronkéntTöbb érték → hiba
FROMEgész táblaKötelező alias
ANY / ALL1 oszlop, több sora MySQL ismeri, az SQLite-gyakorló nem

9Élő SQL-gyakorló

Próbáld ki a beágyazott lekérdezéseket a webshopon! A chipek a különböző formákat töltik be — futtasd, majd alakítsd át (pl. cseréld az AVG-t MAX-ra, vagy az EXISTS-t NOT EXISTS-re).

SQL-gyakorló — beágyazott lekérdezések

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

A motor SQLite-alapú: az IN/NOT IN, EXISTS, korrelált és FROM-beli subquery-k azonosak a MySQL-lel. Két eltérés: az ANY/ALL-t az SQLite nem ismeri (a gyakorló jelzi, és megmutatja a MAX/MIN-es helyettesítést); a több sort adó skalár subquerynél pedig a MySQL hibát dob, az SQLite viszont csendben az első sorral számol — ilyenkor a gyakorló sárga jelzéssel figyelmeztet. (Internetkapcsolat szükséges a motorhoz.)

10Feladatok

Gyakorló feladatok

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

7d.1⭐

Mivel vizsgálod, hogy egy vásárlónak van-e legalább egy rendelése (létezés-vizsgálat)?

7d.2⭐

Mire való a SELECT 1 az EXISTS belsejében?

7d.3⭐⭐

Mivel helyettesíthető a MySQL ar > ALL (...) kifejezése?

7d.4⭐⭐

Mit ad vissza ez a lekérdezés? Előbb gondold végig, aztán futtasd le!

SELECT nev, ar FROM termek
WHERE ar > (SELECT AVG(ar) FROM termek);
7d.5⭐⭐⭐

Miért ad hibát ez a lekérdezés éles MySQL-en (a 3-as kategóriában több termék is van)? Futtasd le a gyakorlóban — figyeld meg a sárga jelzést —, majd javítsd ki kétféleképpen!

SELECT nev FROM termek
WHERE ar = (SELECT ar FROM termek WHERE kategoria_id = 3);
7d.6⭐⭐⭐

Mit csinál ez a lekérdezés, és miért korrelált?

SELECT v.vezeteknev,
       (SELECT COUNT(*) FROM rendeles r WHERE r.vasarlo_id = v.id) AS db
FROM vasarlo v;
7d.7⭐

Kódírás: írj lekérdezést a legdrágább termék(ek) nevére, skalár subqueryvel!

7d.8⭐⭐

Kódírás: add meg azoknak a vásárlóknak a vezetéknevét, akiknek nincs rendelésük — NOT EXISTS használatával!

7d.9⭐⭐⭐

Kódírás: származtatott táblával (FROM-beli subquery) add meg azokat a kategória-azonosítókat, ahol az átlagár 10 000 Ft felett van!

11Önellenőrző kvíz

Ellenőrizd magad!

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

12Fogalomtár

FogalomJelentés
beágyazott lekérdezés (subquery)zárójelbe tett SELECT egy nagyobb lekérdezésen belül
skalár subquerypontosan egy értéket ad — összehasonlításhoz és a SELECT-listába
IN-lista subqueryegy oszlop több sora — a sor akkor felel meg, ha az érték szerepel benne
NOT IN NULL-csapdaegyetlen NULL a listában → 0 sor; helyette NOT EXISTS
EXISTS · SELECT 1létezés-vizsgálat — csak a sor léte számít, az érték nem
korrelált subquerya külső sorra hivatkozik — soronként értékelődik ki
független subqueryönállóan is lefutna — egyszer értékelődik ki
származtatott tábla (derived table)FROM-beli subquery ideiglenes táblaként — kötelező aliasszal
ANY / ALLösszehasonlítás egy oszlopnyi értékre — a MySQL ismeri (a gyakorló nem), MAX/MIN-nel kiváltható

13Összegzés

  1. A beágyazott lekérdezés egy SELECT egy másik lekérdezésen belül; előbb a belső fut le.
  2. Fő helyei: WHERE/HAVING, SELECT-lista, FROM (és INSERT/UPDATE/DELETE is).
  3. Skalár (1 érték): összehasonlításhoz és a SELECT-listába; 1 oszlop: IN-hez; egész tábla: a FROM-ba.
  4. EXISTS a létezést nézi; a NOT EXISTS a NOT IN biztonságos alternatívája.
  5. Korrelált: a külső sorra hivatkozik; független: önállóan is lefutna.
  6. Kötöttségek: skalár helyen csak 1 érték; FROM-ban kötelező alias; NOT IN + NULL csapda; az ANY/ALL-t a gyakorló nem ismeri (a MySQL igen).