Подзаявки
Накратко: Подзаявката е SELECT вътре в друга заявка, който връща едно число или списък за WHERE и се ползва с IN, EXISTS или NOT EXISTS.
Проблем
Над средното
Управителят иска да види продуктите, които са по-скъпи от средната цена в магазина. Ти вече знаеш две заявки: едната намира средната цена, другата търси продуктите над дадено число.
Може да пуснеш първата, да препишеш числото и да пуснеш втората. Но утре цените ще са други, а преписаното число ще е остаряло.
Идеята
Подзаявка
Първо средната цена:
Сега същата заявка, в скоби, на мястото на числото:
Заявка в скоби вътре в друга заявка се казва подзаявка (subquery). Базата първо изпълнява нея, получава 7,036 и после проверява всеки продукт с price > 7.036. Излизат 8 продукта.
Тази подзаявка връща едно число, затова стои там, където би стояло число.
По-точно, за любопитните
Ако подзаявката в сравнение върне няколко реда, SQLite тихо взима първия. В категория 8 („Кухня“) има три продукта, а заявката сравнява само с един от тях:
Кой ред е „първият“, не е обещано. PostgreSQL и SQL Server в този случай дават грешка, че подзаявката връща повече от един ред. Ако подзаявката може да върне няколко реда, ползвай IN.
Пробвай
По-скъпи от кашкавала
Покажи името и цената на продуктите, които са по-скъпи от кашкавала. Не пиши цената му на ръка, а я вземи с подзаявка.
Решение
SELECT name, price FROM products
WHERE price > (SELECT price FROM products WHERE name = 'Кашкавал');WHERE price > (SELECT price FROM products WHERE name = 'Кашкавал'). Кашкавалът е 9,50 €, а по-скъпите са 6, от дъската за рязане (11,40 €) до комплекта ножове (39 €).
Идеята
IN със списък от заявка
IN вече го знаеш със списък, написан на ръка: city IN ('Варна', 'Бургас'). Списъкът може да идва и от подзаявка.
Кои клиенти са поръчвали през октомври 2026 г.? Номерата им са в orders, а имената в customers:
Подзаявката връща номерата на клиентите с поръчки от 1 октомври нататък. Външната заявка показва клиентите, чийто id е в този списък.
Подзаявките могат да се влагат една в друга. Всяка стъпка е отделна заявка, която може да пуснеш и провериш сама.
Пробвай
Кой купува кафе
Доставчикът на кафе пуска нов вкус и иска да пише на хората, които вече са купували „Кафе на зърна“. Покажи имената им. Полето започва от първата стъпка: номера на продукта.
Решение
SELECT name FROM customers
WHERE id IN (
SELECT customer_id FROM orders
WHERE id IN (
SELECT order_id FROM order_items
WHERE product_id = (SELECT id FROM products WHERE name = 'Кафе на зърна')
)
);Три стъпки отвътре навън: номерът на продукта (17), поръчките с ред за него (order_items) и клиентите на тези поръчки (orders). Излизат трима: Мария, Виктория и Симона. Става и с JOIN: сравнява се резултатът, не начинът.
Идеята
EXISTS
Понякога не те интересува какво връща подзаявката, а дали връща нещо. Кои клиенти още нямат нито една поръчка?
EXISTS (…) е вярно, ако подзаявката върне поне един ред. NOT EXISTS е обратното. Какво пише след SELECT в подзаявката, няма значение, затова е SELECT 1.
Тук подзаявката гледа реда отвън: o.customer_id = c.id, където c е клиентът, който външната заявка проверява в момента. Това е нова идея и в следващия урок ще я разгледаме отблизо.
Виж го
NOT IN и един NULL
Управителят подрежда менюто на сайта и пита кои категории нямат нито един продукт. Очаква „Храни“ и „Дом“: те съдържат само подкатегории. Ето първия опит:
Това е капанът от урока за NULL, но сега NULL идва от подзаявката и не се вижда в заявката. NOT EXISTS не сравнява стойности, а пита дали има такъв продукт, и NULL не му пречи:
Когато подзаявката е по колона, която може да е NULL, ползвай NOT EXISTS или добави WHERE category_id IS NOT NULL в подзаявката.
Упражнение
Две задачи
1. Отделът за качество иска да звънне на клиентите, които имат поне една отказана (cancelled) поръчка.
Покажи имената на клиентите с поне една отказана поръчка.
Решение
SELECT name FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.id AND o.status = 'cancelled'
);WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.status = 'cancelled'). Седем клиенти. Става и с id IN (SELECT customer_id FROM orders WHERE status = 'cancelled').
2. Маркетингът подготвя кампания „Напиши отзив“ за продуктите, за които още никой не е писал.
Покажи имената на продуктите без нито един отзив (таблицата reviews).
Решение
SELECT name FROM products p
WHERE NOT EXISTS (SELECT 1 FROM reviews r WHERE r.product_id = p.id);WHERE NOT EXISTS (SELECT 1 FROM reviews r WHERE r.product_id = p.id). 12 продукта, сред тях и подаръчната карта.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: IN, BETWEEN и LIKE · NULL: няма стойност · Агрегатни функции: COUNT, SUM, AVG · JOIN: две таблици в една заявка
- Продължава в: Корелирани подзаявки · Отчет на стъпки: WITH · Дървета и редици: WITH RECURSIVE · Прозоречни функции
- Задачи по темата: Подзаявки и прозорци