Частая задача в SQL — выбрать строки из одной таблицы, если в другой таблице существует хотя бы одна связанная запись.
Таблицы:
customers(id, name)
orders(id, customer_id, created_at)
Нужно получить клиентов, у которых есть хотя бы один заказ.
Попытка через
JOIN:SELECT
c.id,
c.name
FROM customers c
JOIN orders o
ON o.customer_id = c.id;
Запрос вернёт клиентов, но если у клиента несколько заказов, он появится в результате несколько раз.
Чтобы убрать дубликаты, часто добавляют
DISTINCT:SELECT DISTINCT
c.id,
c.name
FROM customers c
JOIN orders o
ON o.customer_id = c.id;
Результат будет корректным, однако такой запрос выражает задачу менее точно:
JOIN формирует строки для каждого совпадения, а DISTINCT затем устраняет повторяющиеся значения.В подобных задачах фактически требуется semi join — вернуть строку из
customers, если существует хотя бы одна связанная строка в orders.В большинстве СУБД это обычно выражают через
EXISTS:SELECT
c.id,
c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);
Подзапрос проверяет наличие хотя бы одной строки в
orders, связанной с текущим клиентом.Оптимизатор часто может завершить проверку сразу после нахождения первого совпадения, поэтому
EXISTS является естественным инструментом для проверки существования строк.Практический пример — клиенты, которые делали заказы после определённой даты:
SELECT
c.id,
c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
AND o.created_at >= '2025-01-01'
);
EXISTS не возвращает данные из подзапроса — он только проверяет факт существования строки.Поэтому внутри обычно пишут:
SELECT 1
или:
SELECT *
В контексте
EXISTS список выбираемых выражений не влияет на результат.🔥
EXISTS — стандартный и выразительный способ реализовать semi join. Он точно отражает намерение запроса и часто позволяет оптимизатору построить более эффективный план выполнения.➡️ SQL Ready | #практика