JavaRush /Курсы /SQL SELF /Проверка существования данных с EXISTS и <...

Проверка существования данных с EXISTS и NOT EXISTS

SQL SELF
13 уровень , 3 лекция
Открыта

Добро пожаловать на новую лекцию по SQL! Сегодня мы познакомимся с самыми незаметными, но невероятно мощными операторамиEXISTS и NOT EXISTS. Представьте себе разведчика, который не оставляет следов, но мгновенно сообщает: "Да, объект существует" или "Нет, здесь пусто". Эти операторы не возвращают данных напрямую, но позволяют делать логически точные проверки в запросах.

Начнём с основ. EXISTS — это оператор, который проверяет существование записей в результате выполнения подзапроса. Если подзапрос возвращает хотя бы одну запись, условие EXISTS вернёт TRUE, в противном случае — FALSE.

SELECT 1
WHERE EXISTS (
    SELECT * 
    FROM students 
    WHERE grade > 3.5
);

Как видите, мы не интересуемся самими данными подзапроса, а только тем, что такие строки существуют. Если хоть одна запись соответствует условию, запрос возвращает 1.

Синтаксис EXISTS

Синтаксис EXISTS выглядит просто:

SELECT колонки
FROM таблица
WHERE EXISTS (
    SELECT 1
    FROM другая_таблица
    WHERE условие
);

Объяснение:

  • Вложенный подзапрос внутри EXISTS может быть любым запросом.
  • Именно результат подзапроса определяет, вернётся ли TRUE или FALSE.

Пример: Есть ли студенты с оценкой выше 4?

Представим себе таблицу students:

id name grade
1 Otto 3.2
2 Anna 4.7
3 Dan 5.0
4 Lina 2.9

Допустим, мы хотим проверить, существуют ли студенты с оценкой выше 4. Используем следующий запрос:

SELECT 'Есть студенты с высоким баллом!'
WHERE EXISTS (
    SELECT 1
    FROM students
    WHERE grade > 4
);

Результат:

Есть студенты с высоким баллом!

Почему EXISTS работает быстрее, чем IN?

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

Например, представьте, что в таблице students миллионы записей, но мы ищем только одно существующее совпадение (grade > 4). Как только SQL найдет первую строку, подходящую по условию, запрос завершится.

Использование NOT EXISTS

Теперь давайте поговорим о NOT EXISTS. Этот оператор работает как антипод EXISTS. Он возвращает TRUE, если подзапрос не возвращает ни одной записи.

Пример: найти студентов без оценок (NULL)

Допустим, в нашей таблице есть студенты, у которых ещё нет оценок:

id name grade
1 Otto NULL
2 Anna 4.7
3 Dan 5.0
4 Lina NULL

Мы хотим выбрать всех студентов с отсутствующими оценками. Используем NOT EXISTS:

SELECT *
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM students 
    WHERE grade IS NOT NULL
    AND id = s.id
);

Результат:

id name grade
1 Otto NULL
4 Lina NULL

Сравнение EXISTS и IN

Иногда кажется, что EXISTS и IN делают одно и то же. На первый взгляд — да, но есть нюансы. Особенно если где-то рядом затесался NULL. Тогда поведение IN может стать неожиданным, а EXISTS — спасителем.

Давайте посмотрим на примере.

Таблица courses (курсы, которые можно пройти):

course_id name
1 Математика
2 История

А вот и студенты:

student_id name
1 Alex Lin
2 Anna Song
3 Maria Chi
4 Dan Seth
5 Shadow Moon

Таблица enrollments (записи о том, кто на какой курс записался):

student_id course_id
1 1
2 2
3 NULL

Мы хотим выбрать названия курсов, на которые кто-то записан. Вроде бы просто.

С использованием IN:

SELECT name
FROM courses
WHERE course_id IN (
    SELECT course_id 
    FROM enrollments
);

На первый взгляд всё должно сработать. Но если в enrollments есть NULL в courseid, как у Maria Chi, IN может вернуть... ничего! Потому что NULL делает подзапрос "неопределённым", и SQL теряется: вдруг NULL — это именно тот courseid, которого мы ищем?

С использованием EXISTS:

SELECT name
FROM courses c
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE c.course_id = e.course_id
);

А вот EXISTS проверяет: "Есть ли хотя бы одна строка, где course_id совпадает?" — и всё. Он не смущается, если где-то рядом NULL, ведь он ищет конкретные совпадения, а не список значений.

Итог: если в подзапросе может быть NULL, лучше использовать EXISTS, чтобы не получить сюрпризов.

Примеры реальных задач

Таблица students:

id name
1 Alex Lin
2 Anna Song
3 Maria Chi
4 Dan Seth
5 Shadow Moon

Таблица enrollments:

student_id course_id
1 1
2 2
3 NULL

Пример 1. Студенты, записанные на курсы

Найдём тех, кто где-то уже засветился — записался на курс (пусть даже странно, как Maria Chi):

SELECT name
FROM students s
WHERE EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE s.id = e.student_id
);

Результат:

Alex Lin
Anna Song
Maria Chi

Если студент хоть как-то фигурирует в enrollments — он попадает в выборку, даже если его course_id непонятен.

Пример 2. Студенты без курсов

А теперь найдём тех, кто пока что просто существует в системе — но никуда не записан:

SELECT name
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE s.id = e.student_id
);

Результат:

Dan Seth
Shadow Moon

Похоже, эти двое пока не нашли курс по душе. Или просто забыли, что надо записываться :)

Пример 3. Выборка курсов с более чем 5 зарегистрированными студентами

Таблица courses:

course_id name
1 Математика
2 История
3 Биология
4 Философия

Таблица enrollments:

student_id course_id
1 1
2 1
3 1
4 1
5 1
6 1
7 2
8 2
9 2
10 NULL

Мы хотим найти такие курсы, на которые записалось больше пяти студентов. Тут EXISTS как бы спрашивает: "А есть ли у этого курса хотя бы одна группа записей, где студентов больше пяти?"

SELECT name
FROM courses c
WHERE EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE c.course_id = e.course_id
    GROUP BY e.course_id
    HAVING COUNT(*) > 5
);

Результат:

Математика

Только на курс "Математика" (course_id = 1) записано шесть студентов. Остальные курсы пока не столь популярны.

Частые ошибки при использовании EXISTS и NOT EXISTS

  1. Неправильное понимание синтаксиса подзапроса. Всегда проверяйте, что подзапрос корректно ссылается на внешнюю таблицу.
  2. Забытая проверка NULL. Даже если используете EXISTS, иногда вам нужно явно указать обработку NULL.
  3. Отсутствие индекса на полях подзапроса. Это может значительно замедлить выполнение запроса.

На этом всё на сегодня! Теперь вы знаете, как использовать EXISTS и NOT EXISTS для проверки существования данных, а также различия между этими операторами и IN. В следующей лекции мы продолжим погружение в подзапросы, рассмотрев их использование в SELECT для работы с агрегированными данными.

2
Задача
SQL SELF, 13 уровень, 3 лекция
Недоступна
Поиск студентов без оценок
Поиск студентов без оценок
2
Задача
SQL SELF, 13 уровень, 3 лекция
Недоступна
Проверка курсов с зарегистрированными студентами
Проверка курсов с зарегистрированными студентами
Комментарии (8)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anonymous #3210996 Уровень 27
13 декабря 2025
Нашел лучшее обьяснение как работает NOT EXISTS. " Исключает строки соответствующие подзапросу".
Сергей Третяк Уровень 14
2 ноября 2025
во второй задаче,если таблице courses присвоить алиас, на пример: 'courses c' то проверка выдает ошибку, хотя все работает правильно. Исправьте баг.
Anonymous #3449047 Уровень 61
21 сентября 2025
У вас двоечники писали задачи или такие же вкатчики в айти выпускники курсов?
Slevin Уровень 1
5 сентября 2025
Представьте себе разведчика, который не оставляет следов, но мгновенно сообщает: "Да, объект существует" или "Нет, здесь пусто". О нет! Только не возвращение шутника из лекций по Python. "No, God please, no!" 🤣
Denis Murashko Уровень 22
27 августа 2025
1я задача все еще не работает
Евгений Уровень 49 Expert
29 июля 2025
В лекции приводится вот такая информация: На первый взгляд всё должно сработать. Но если в enrollments есть NULL в courseid, как у Maria Chi, IN может вернуть... ничего! Потому что NULL делает подзапрос "неопределённым", и SQL теряется: вдруг NULL — это именно тот courseid, которого мы ищем? И запрос:

SELECT name
FROM courses
WHERE course_id IN (
    SELECT course_id
    FROM enrollments
);
Вообще данный запрос отработает корректно. То есть он вернёт все курсы из таблицы в примере. Можете попробовать сами мой дополненный пример:

DROP TABLE IF EXISTS courses;
DROP TABLE IF EXISTS enrollments;

CREATE TABLE courses
(
    course_id INTEGER null,
    name      TEXT    NULL
);

CREATE TABLE enrollments
(
    student_id INTEGER NOT NULL,
    course_id  INTEGER NULL
);

insert into courses(course_id, name)
values (1, 'math'),
       (2, 'history'),
       (null, 'test');

insert into enrollments(student_id, course_id)
values (1, 1),
       (2, 2),
       (3, NULL);

SELECT name
FROM courses
WHERE course_id IN (SELECT course_id
                    FROM enrollments);
Вероятно, в лекции перепутали, потому что наличие NULL в списке, который проверяется в IN, действительно приводит к пустым результатам, но не в случае IN, а в случае NOT IN. В комментарии разъясняю подробнее, с примером.
Евгений Уровень 49 Expert
29 июля 2025
Итак, у нас есть подзапрос:

SELECT course_id
FROM enrollments
Он возвращает вот такой список (для примера):

(1, 2, NULL)
И представим, что у нас есть таблица courses, которая, допустим, содержит только две записи со следующими значениями course_id:

(3, 2)
Для простоты можем интерпретировать следующий запрос:

SELECT name
FROM courses
WHERE course_id IN (SELECT course_id
                    FROM enrollments);
вот в таком виде:

SELECT name
FROM courses
WHERE course_id IN (1, 2, NULL);
Что данный запрос вернёт? Он вернёт курс с ID 2. Иначе говоря, наш IN можно интерпретировать вот так:

course_id = 1 OR course_id = 2 OR course_id = NULL
При ID курса 2 мы получим true. При ID курса 3 мы получим UNKNOWN, потому что сравнения с 1 и 2 вернут false, а вот сравнение с NULL произвести невозможно. А теперь перейдём к такому же запросу, только поменяем IN на NOT IN. В данном случае сравнение будет выглядеть следующим образом:

course_id <> 1 AND course_id <> 2 AND course_id <> NULL
В IN мы находим только первое попавшееся совпадение, а в NOT IN значение сравнивается со всем содержимым списка. А так как сравнение с NULL всегда UNKNOWN, то и результат выполнения NOT IN со списком, содержащим NULL, всегда будет пустым. Подробнее можно прочитать вот здесь.
Юрий Уровень 60
30 июня 2025
Задача "Поиск студентов без оценок" Содержит в проверка взаимоисключающие задания. И не проходит проверку даже если вбить ответ ) Исправьте, пожалуйста.