EXISTS и NOT EXISTS
Эффективно проверяйте наличие связанных строк
«EXISTS и NOT EXISTS» — бесплатный урок SQL Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Academy содержит 4 уроков всего.
Что такое EXISTS
Оператор EXISTS проверяет, возвращает ли подзапрос хотя бы одну строку. Он принимает значение TRUE, если подзапрос выдаёт какой-либо результат, и FALSE, если подзапрос пуст.
В отличие от других операторов подзапросов, сравнивающих значения, EXISTS интересует только наличие — он не анализирует фактические данные, возвращённые подзапросом.
Создание примеров таблиц
Перед написанием запросов с EXISTS создадим две таблицы: customers и orders. На протяжении всего урока мы будем использовать их, чтобы на практике изучить работу EXISTS и NOT EXISTS.
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
name VARCHAR(100),
country VARCHAR(50)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
amount DECIMAL(10,2),
order_date DATE
);
INSERT INTO customers VALUES
(1, 'Alice', 'US'),
(2, 'Bob', 'UK'),
(3, 'Charlie', 'US'),
(4, 'Diana', 'DE');
INSERT INTO orders VALUES
(101, 1, 250.00, '2024-01-10'),
(102, 1, 180.00, '2024-02-15'),
(103, 2, 95.00, '2024-03-01'),
(104, 3, 430.00, '2024-03-22');Базовый синтаксис EXISTS
В базовом синтаксисе EXISTS помещается в предложение WHERE. Подзапрос внутри EXISTS обычно обращается к столбцу из внешнего запроса — такой подзапрос называется коррелированным подзапросом.
Приведённый ниже запрос находит каждого клиента, разместившего хотя бы один заказ.
SELECT customer_id, name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);SELECT 1 внутри EXISTS
Возможно, Вы заметили, что в подзапросе используется SELECT 1, а не выбирается какой-либо настоящий столбец. Это сделано намеренно: EXISTS проверяет только, существуют ли строки, а не то, что они содержат.
Использование SELECT 1 (или даже SELECT *) не влияет на результат, но SELECT 1 ясно показывает и движку базы данных, и читателю, что сами значения не важны.
-- Both of these return the same result
SELECT name FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);
SELECT name FROM customers c
WHERE EXISTS (SELECT o.* FROM orders o WHERE o.customer_id = c.customer_id);Как база данных вычисляет EXISTS
Для каждой строки внешнего запроса база данных выполняет коррелированный подзапрос. Как только найдена одна подходящая строка, движок прекращает сканирование и помечает EXISTS как TRUE — это вычисление с коротким замыканием делает EXISTS очень эффективным даже в больших таблицах.
В отличие от этого, JOIN сначала сформировал бы полный набор подходящих строк, а затем отфильтровал бы его, что может быть медленнее, если нужно лишь узнать, существует ли совпадение.
-- EXISTS short-circuits after first match
-- Efficient even when orders table has millions of rows
SELECT name, country
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.amount > 200
);NOT EXISTS: поиск отсутствующих строк
NOT EXISTS — это противоположная проверка: она возвращает TRUE, когда подзапрос не находит ни одной подходящей строки. Это стандартный способ SQL ответить на вопросы вроде «какие клиенты никогда не размещали заказ?»
Попытка решить такую задачу с помощью обычного JOIN или NOT IN может привести к неверным результатам при наличии NULL — NOT EXISTS полностью позволяет избежать этой проблемы.
-- Customers who have NOT placed any order
SELECT customer_id, name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);NOT EXISTS и NOT IN при наличии NULL
Одно из важных преимуществ NOT EXISTS перед NOT IN — безопасная работа с NULL. Если используемый NOT IN подзапрос возвращает хотя бы один NULL, всё выражение NOT IN становится равным NULL, а значит, внешний запрос не возвращает ни одной строки.
NOT EXISTS не подвержен этой проблеме, поскольку проверяет существование строк, а не равенство значений.
-- Dangerous: if any customer_id in orders is NULL,
-- NOT IN returns zero rows!
SELECT name FROM customers
WHERE customer_id NOT IN (SELECT customer_id FROM orders);
-- Safe: NOT EXISTS handles NULLs correctly
SELECT name FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
);EXISTS с несколькими условиями
Подзапрос внутри EXISTS может содержать любую допустимую конструкцию SQL, в том числе несколько условий WHERE. Это позволяет проверять очень конкретные связанные строки — например, находить клиентов, разместивших в определённом месяце заказ на сумму выше заданного порога.
-- Customers who placed an order over 200 in March 2024
SELECT c.name, c.country
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.amount > 200
AND o.order_date >= '2024-03-01'
AND o.order_date < '2024-04-01'
);EXISTS в DELETE и UPDATE
EXISTS используется не только в инструкциях SELECT. Его можно применять в UPDATE и DELETE, чтобы изменять или удалять строки на основании наличия связанных данных в другой таблице.
Пример ниже удаляет заказы, принадлежащие клиентам из определённой страны.
-- Delete orders placed by US customers
DELETE FROM orders o
WHERE EXISTS (
SELECT 1
FROM customers c
WHERE c.customer_id = o.customer_id
AND c.country = 'US'
);Сравнение EXISTS и JOIN
EXISTS и JOIN часто позволяют сформулировать один и тот же вопрос, но работают по-разному. JOIN создаёт несколько копий строк, если найдено несколько совпадений, а EXISTS возвращает каждую строку внешнего запроса не более одного раза.
Если Вам нужно лишь узнать, существует ли связь, а не получить данные из связанной таблицы, EXISTS проще и обычно быстрее.
-- JOIN may return duplicate customer rows if a customer has multiple orders
SELECT DISTINCT c.name
FROM customers c
JOIN orders o ON o.customer_id = c.customer_id;
-- EXISTS always returns each customer once
SELECT c.name
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
);Проверка качества данных с помощью NOT EXISTS
NOT EXISTS — мощный инструмент для аудита качества данных. С его помощью можно находить осиротевшие записи, отсутствующие ссылки или строки, для которых должны существовать связанные данные, но их нет.
Запрос ниже обнаруживает строки заказов, в которых customer_id не соответствует ни одной строке в таблице клиентов, — это признак нарушения ссылочной целостности.
-- Find orders with no matching customer (orphaned records)
SELECT o.order_id, o.customer_id, o.amount
FROM orders o
WHERE NOT EXISTS (
SELECT 1
FROM customers c
WHERE c.customer_id = o.customer_id
);Быстрая проверка
Проверьте, насколько Вы поняли принцип работы EXISTS и NOT EXISTS.
Итоги урока
В этом уроке Вы узнали, как EXISTS и NOT EXISTS позволяют проверять наличие или отсутствие связанных строк без прямого сравнения значений.
Основные выводы:
- EXISTS возвращает TRUE, как только подзапрос находит одну подходящую строку (вычисление с коротким замыканием).
- NOT EXISTS возвращает TRUE, когда подзапрос не находит подходящих строк.
- Используйте
SELECT 1внутри EXISTS — возвращаемые значения не имеют значения. - NOT EXISTS безопасно работает с NULL, а NOT IN — нет; предпочитайте NOT EXISTS, если могут встретиться NULL.
- EXISTS работает в инструкциях SELECT, UPDATE и DELETE.
- Если Вам нужно только проверить наличие, EXISTS часто проще и быстрее, чем JOIN.
Часто задаваемые вопросы
Урок «EXISTS и NOT EXISTS» бесплатный?
Да — полный текст урока «EXISTS и NOT EXISTS» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Academy, подпишись на CoddyKit PRO. Курс SQL Academy содержит 4 уроков всего.
Чему я научусь в уроке «EXISTS и NOT EXISTS»?
Эффективно проверяйте наличие связанных строк Ты практикуешь SQL Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать SQL Academy?
Предыдущий опыт не требуется. SQL Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «EXISTS и NOT EXISTS»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке SQL Academy?
Да. Каждый урок SQL Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Коррелированные подзапросы
- EXISTS и NOT EXISTS
- IN, ANY и ALL
- Производительность EXISTS и JOIN