CodeRang
Темы SQL

Подзапросы SQL: EXISTS, IN и коррелированные запросы

Подзапросы SQL позволяют использовать результат одного SELECT внутри другого запроса. На примере EXISTS и NOT EXISTS разберём коррелированный подзапрос, связь с внешней строкой и проверку наличия связанных записей.

Обновлено · Команда SQL Arena

Что потренируете

  • EXISTS и NOT EXISTS
  • IN и подзапросы со списком значений
  • Коррелированные подзапросы
  • Скалярные подзапросы
  • Подзапросы в FROM

Пример задачи в тренажёре

Так выглядит задача в SQL Arena: условие, редактор, результат и схема базы.

Пред.След.Случайная
ЗаданиеОтправкиAI Mentor
Клиенты с оплатами и без возвратов

Найдите клиентов, у которых есть хотя бы один заказ со статусом paid и нет ни одного заказа со статусом refunded. Верните customer_name и city. Отсортируйте результат по имени клиента.

Запрос 1PostgreSQLv16
SELECT c.customer_name, c.city
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.status = 'paid'
)
AND NOT EXISTS (
  SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.status = 'refunded'
)
ORDER BY c.customer_name;
Отправить
Результатcustomersorders
Ответ принят

Как работают подзапросы SQL с EXISTS

Внешний запрос перебирает строки customers. Первый подзапрос должен ответить только на один вопрос: существует ли для текущего клиента хотя бы один заказ со статусом paid. Поэтому здесь удобно использовать EXISTS. Внутри него условие o.customer_id = c.id связывает подзапрос с текущей строкой внешней таблицы. Именно эта ссылка на c.id делает подзапрос коррелированным.

SELECT 1 внутри EXISTS не означает, что база реально возвращает единицу пользователю. Для EXISTS важно только наличие подходящей строки. Можно встретить SELECT *, SELECT id или другой вариант, но значение списка SELECT здесь не используется. Условие status = 'paid' оставляет только оплаченные заказы, а EXISTS становится истинным после нахождения подходящей записи.

Вторая проверка устроена наоборот. NOT EXISTS требует, чтобы для текущего клиента не нашлось ни одного refunded-заказа. В результате Alice, Clara, Eva и Felix проходят обе проверки. Bruno исключается из-за возврата, хотя оплаченный заказ у него есть. Daniel не подходит, потому что у него нет paid-заказа. Такая формулировка обычно читается ближе к бизнес-условию, чем попытка получить тот же результат одним большим JOIN с последующей группировкой.

SQLPostgreSQL v16
SELECT c.customer_name, c.city
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.status = 'paid'
)
AND NOT EXISTS (
  SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.status = 'refunded'
)
ORDER BY c.customer_name;
Результат запроса
customer_namecity
AliceBerlin
ClaraPrague
EvaTallinn
FelixVienna

Типичные ошибки в подзапросах SQL

01

Забыть связь с внешней строкой

Если убрать o.customer_id = c.id, EXISTS проверит наличие хотя бы одного paid-заказа вообще во всей таблице. Если такой заказ существует, условие станет истинным для каждого клиента и перестанет отвечать на вопрос задачи.

02

Использовать NOT IN без учёта NULL

Конструкция x NOT IN (подзапрос) может неожиданно не вернуть строки, если подзапрос содержит NULL. Из-за трёхзначной логики SQL сравнение становится UNKNOWN. Для проверки отсутствия связанных записей коррелированный NOT EXISTS часто безопаснее и понятнее.

03

Ждать одно значение от многорядного подзапроса

Скалярный подзапрос в выражении должен вернуть не больше одной строки. Если написать salary > (SELECT salary FROM ...), а внутренний запрос вернёт несколько зарплат, PostgreSQL выдаст ошибку. В таких случаях нужен агрегат, IN, EXISTS или другая формулировка.

Задачи по теме в тренажёре

Курс «SQL с нуля»

После EXISTS полезно отдельно потренировать скалярные подзапросы, IN, подзапросы в FROM и сравнение строки с агрегатом по другим строкам. В SQL Arena встречаются задачи на максимальные значения, выбор самой дорогой покупки пользователя, поиск клиентов без связанных посещений и вычисления по вложенным выборкам. Если подзапросы пока смешиваются с JOIN и CTE, тему можно пройти последовательно в курсе «SQL с нуля»: теория сопровождается задачей в каждом уроке, а первые главы доступны бесплатно. В самом тренажёре 1 192 задачи. Есть отдельные песочницы PostgreSQL, MySQL, ClickHouse и BigQuery. Ничего устанавливать не требуется. Проверка запускается и на скрытых данных, поэтому случайно подогнанный под видимые строки запрос не пройдёт. Бесплатно доступны 292 задачи на всех четырёх диалектах. Если решение не складывается, AI-ментор может давать подсказки и объяснять ошибки; бесплатно доступны 10 обращений всего.

Справочник по теме

Подробные статьи с синтаксисом и примерами в справочнике SQL Arena.

Частые вопросы

Что такое подзапросы SQL?

Подзапрос — это SELECT, вложенный в другой SQL-запрос. Его можно использовать в WHERE, FROM, SELECT и других частях запроса в зависимости от того, какой результат он возвращает.

Как работают подзапросы SQL?

Некоррелированный подзапрос может быть вычислен независимо от внешнего запроса. Коррелированный подзапрос обращается к значениям текущей строки внешнего запроса, поэтому логически проверяется относительно каждой такой строки.

Что такое коррелированный подзапрос SQL?

Коррелированный подзапрос содержит ссылку на столбец внешнего запроса. Например, условие o.customer_id = c.id связывает строки orders с текущей строкой customers.

Когда использовать EXISTS в подзапросе SQL?

EXISTS удобно использовать, когда важен сам факт наличия хотя бы одной подходящей строки. Он возвращает истинное значение, если подзапрос может вернуть хотя бы одну строку.

Чем EXISTS отличается от IN в SQL?

IN сравнивает значение с набором значений, а EXISTS проверяет наличие строки, удовлетворяющей условиям подзапроса. В некоторых задачах оба варианта могут выражать похожую логику, но при работе с NULL и коррелированными условиями их поведение и форма запроса различаются.

Практика подзапросов SQL на задачах

Попробуйте задачи на EXISTS, NOT EXISTS, IN, скалярные и коррелированные подзапросы в SQL Arena. Если нужна последовательная база от SELECT до вложенных запросов, продолжите в курсе «SQL с нуля».

Решать задачи на подзапросы