Клиенты, бравшие боевики
Задача
Маркетинг запускает акцию по боевикам: «Дай список клиентов, которые хоть раз брали фильм из категории Action. Каждого — один раз, дубли не нужны. Пришлём им промокод на новинки жанра».
Верни имя и фамилию клиентов, у которых была хотя бы одна аренда фильма категории Action.
Схема данных
Учебная база проката dvdrental:
customer(customer_id, first_name, last_name, ...)— клиенты.rental(rental_id, inventory_id, customer_id, ...)— аренды.inventory(inventory_id, film_id, ...)— копии дисков.film_category(film_id, category_id)иcategory(category_id, name)— жанры.
Решение
SELECT c.first_name, c.last_name
FROM customer c
WHERE EXISTS (
SELECT 1
FROM rental r
JOIN inventory i USING (inventory_id)
JOIN film_category fc USING (film_id)
JOIN category cat USING (category_id)
WHERE r.customer_id = c.customer_id
AND cat.name = 'Action'
)
ORDER BY c.last_name, c.first_name, c.customer_id
LIMIT 20;
Разбор
Вопрос звучит как «существует ли у клиента хотя бы одна аренда боевика?» — и EXISTS переводит его в SQL почти дословно. EXISTS (...) возвращает true, если подзапрос нашёл хоть одну строку, и false, если ни одной. Клиент попадает в ответ, когда для него подзапрос что-то нашёл.
Подзапрос коррелированный — он связан с внешней строкой через WHERE r.customer_id = c.customer_id: для каждого клиента он ищет именно его аренды боевиков. Внутри — цепочка от аренды к жанру: rental → inventory (какой фильм) → film_category → category, и фильтр cat.name = 'Action'. Обрати внимание на SELECT 1: что именно выбирать в EXISTS, неважно — движок проверяет только сам факт наличия строк, а не их содержимое. Пишут 1 (или *) просто по договорённости, «нам нужно да/нет».
Почему EXISTS, а не обычный JOIN клиента с боевиками? Джойн размножил бы клиента по числу взятых им боевиков, и пришлось бы навешивать DISTINCT, чтобы убрать дубли. EXISTS этого не делает: он не тянет строки в результат, а лишь отвечает, есть они или нет, — поэтому каждый клиент выходит ровно один раз, без DISTINCT. Зеркальный приём, «найди тех, у кого совпадений нет», — это NOT EXISTS или анти-джойн через LEFT JOIN … IS NULL, как в задаче фильмы без единой копии.
ORDER BY c.last_name, c.first_name, c.customer_id даёт стабильный порядок (третий ключ разводит однофамильцев), LIMIT 20 отрезает витрину для примера.
Ожидаемый результат
first_name | last_name
--- | ---
Nathaniel | Adam
Kathleen | Adams
Gordon | Allard
Shirley | Allen
Charlene | Alvarez
Lisa | Anderson
Jose | Andrew
Ida | Andrews
Harry | Arce
Jordan | Archuleta
… всего строк: 20 (показаны первые 10)
Прорешай такие задачи сам
Читать разбор полезно, а настоящая прокачка — самому писать запросы и получать обратную связь. В симуляторе живая база и проверка запросов, лёгкий режим — бесплатно.
Прорешать в бесплатной песочнице