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 для роботи з агрегованими даними.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ