Защо пак подзаявки
В Модул 3 (урок „Влагане на заявки“) видяхте основната идея: вътрешен SELECT в скоби на мястото на стойност или на таблица. Тогава работехте в Access с прости примери. Сега ще подредите картината докрай: подзаявката може да стои на три места — в WHERE, в SELECT и във FROM — и на всяко от тях се държи различно. Ще видите и корелираната подзаявка, която „гледа“ реда на външната. След три седмици ще карате AI асистент да ви пише заявки; за да откриете грешката му, трябва да четете подзаявки свободно.
Подзаявка (subquery)
SELECT, вложен в друга заявка и затворен в кръгли скоби. Външната заявка използва резултата му като стойност, като списък от стойности или като таблица.
| Място | Какво връща подзаявката | Как се ползва |
|---|---|---|
WHERE | една стойност / една колона / „има ли редове“ | сравнение (=, >, <), IN, EXISTS |
SELECT | точно една стойност за всеки външен ред | изчислена колона в резултата |
FROM | цяла таблица (много редове и колони) | производна таблица с псевдоним, върху която се работи |
Подзаявка в WHERE
Скаларна — една стойност
Най-простият случай: подзаявката връща едно число и го сравняваме с колона. Класиката „над средния успех“ не може да се напише без подзаявка, защото WHERE grade > AVG(grade) е грешка — агрегатите не се допускат в WHERE.
SELECT name, grade
FROM students
WHERE grade > (SELECT AVG(grade) FROM students);Колона — списък със IN
Ако подзаявката връща много редове, сравнението със = е грешка. Ползвате IN („в списъка“) или NOT IN.
-- Ученици, които имат поне една оценка по БЕЛ (предмет 3)
SELECT name
FROM students
WHERE id IN (SELECT student_id FROM marks WHERE subject_id = 3);EXISTS — „има ли поне един такъв ред“
EXISTS не гледа стойностите, а само дали подзаявката връща нещо. Затова вътре се пише SELECT 1. Почти винаги е корелирана: условието вътре споменава ред от външната заявка.
-- Ученици без нито една оценка
SELECT name
FROM students s
WHERE NOT EXISTS (SELECT 1 FROM marks m WHERE m.student_id = s.id);Подзаявка в SELECT
Подзаявка в списъка на колоните трябва да върне точно една стойност за всеки ред на външната заявка. Обикновено е корелирана — пресмята нещо за текущия ученик, клас, предмет.
SELECT s.name,
(SELECT COUNT(*) FROM marks m WHERE m.student_id = s.id) AS broy_ocenki
FROM students s;- Задължително давайте име с
AS— иначе колоната се казва колкото целия израз. - Ако подзаявката не намери редове:
COUNTвръща 0, аMAX,AVGи обикновенSELECTвръщатNULL. Ще го видите във втория пример при учениците без оценки. - Ако върне повече от един ред — грешка (в SQLite мълчаливо се взема първият, което е още по-лошо: грешен резултат без грешка).
Подзаявка във FROM — производна таблица
На мястото на таблица може да застане цял SELECT. Резултатът му е производна таблица (derived table) — съществува само по време на заявката. Най-честата употреба: първо групирате, после работите с обобщените стойности като с обикновени колони — съединявате ги, филтрирате ги, сортирате ги.
SELECT c.name AS klas, t.sreden
FROM (
SELECT class_id, ROUND(AVG(grade), 2) AS sreden
FROM students
GROUP BY class_id
) AS t
JOIN classes c ON c.id = t.class_id
WHERE t.sreden > 5;Корелирана подзаявка
Корелирана подзаявка
Подзаявка, чието условие използва колона от външната заявка (например s2.class_id = s.class_id). Не може да се изпълни сама — пресмята се наново за всеки ред на външната.
-- Ученици с успех над средния ЗА СВОЯ КЛАС
SELECT s.name, s.grade
FROM students s
WHERE s.grade > (SELECT AVG(s2.grade)
FROM students s2
WHERE s2.class_id = s.class_id);Сравнете с първия пример: там средният успех е едно число за цялото училище и подзаявката се пресмята веднъж. Тук за всеки ученик се пресмята средният успех на неговия клас — псевдонимите s и s2 са двете „копия“ на таблицата, както при SELF JOIN от Модул 3.
Подзаявка или JOIN
| Подзаявка | JOIN | |
|---|---|---|
| Кога е естествена | проверка „дали има“, сравнение с обобщена стойност, филтър по списък | трябват ви колони от двете таблици в резултата |
| Дублиране на редове | няма — външната таблица си остава с редовете си | при 1:M редовете се умножават; трябва DISTINCT или GROUP BY |
| Четимост | следва въпроса на български: „ученици, за които има…“ | по-кратка при много таблици |
| Скорост | корелираната се изпълнява за всеки ред | обикновено един проход; оптимизаторът я обича |
Какво трябва да запомниш
- Подзаявка = SELECT в скоби на мястото на стойност, списък или таблица.
- В WHERE: една стойност → сравнение; колона → IN / NOT IN; „има ли“ → EXISTS / NOT EXISTS.
- В SELECT: точно една стойност за всеки външен ред, винаги с AS; липса → NULL (COUNT → 0).
- Във FROM: производна таблица — AS имена на колоните и псевдоним на таблицата (в MySQL задължително).
- Корелираната подзаявка споменава външния ред и се изпълнява за всеки ред — удобна, но скъпа.
- JOIN, когато ви трябват колони от двете таблици; подзаявка, когато проверявате или сравнявате.