0Pricing
SQL Academy · Урок

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 — локальная установка не требуется.

Все уроки этого курса

  1. Коррелированные подзапросы
  2. EXISTS и NOT EXISTS
  3. IN, ANY и ALL
  4. Производительность EXISTS и JOIN
← Назад к SQL Academy