Це дуже гарне запитання, тому я вирішив написати дуже детальну статтю на цю тему у своєму блозі.
Модель таблиці баз даних
Припустимо, у нашій базі даних є дві наступні таблиці, які утворюють табличне відношення «один до багатьох».
student
Таблиця є батьком, і student_grade
є дочірньою таблицею , так як він має student_id стовпець зовнішнього ключа , який посилається Ід стовпець первинного ключа в таблиці студента.
student table
Містить наступні два записи:
| id | first_name | last_name | admission_score |
|----|------------|-----------|-----------------|
| 1 | Alice | Smith | 8.95 |
| 2 | Bob | Johnson | 8.75 |
А в student_grade
таблиці зберігаються оцінки, які студенти отримали:
| id | class_name | grade | student_id |
|----|------------|-------|------------|
| 1 | Math | 10 | 1 |
| 2 | Math | 9.5 | 1 |
| 3 | Math | 9.75 | 1 |
| 4 | Science | 9.5 | 1 |
| 5 | Science | 9 | 1 |
| 6 | Science | 9.25 | 1 |
| 7 | Math | 8.5 | 2 |
| 8 | Math | 9.5 | 2 |
| 9 | Math | 9 | 2 |
| 10 | Science | 10 | 2 |
| 11 | Science | 9.4 | 2 |
SQL EXISTS
Скажімо, ми хочемо отримати всіх учнів, які отримали 10 клас у класі математики.
Якщо нас цікавить лише ідентифікатор студента, ми можемо запустити такий запит:
SELECT
student_grade.student_id
FROM
student_grade
WHERE
student_grade.grade = 10 AND
student_grade.class_name = 'Math'
ORDER BY
student_grade.student_id
Але програма зацікавлена у тому, щоб відобразити повне ім’я student
, а не лише ідентифікатор, тому нам потрібна інформація також із student
таблиці.
Для фільтрації student
записів, які мають 10-й клас з математики, ми можемо використовувати оператор EXISTS SQL, наприклад, такий:
SELECT
id, first_name, last_name
FROM
student
WHERE EXISTS (
SELECT 1
FROM
student_grade
WHERE
student_grade.student_id = student.id AND
student_grade.grade = 10 AND
student_grade.class_name = 'Math'
)
ORDER BY id
Запускаючи запит вище, ми бачимо, що вибрано лише рядок Alice:
| id | first_name | last_name |
|----|------------|-----------|
| 1 | Alice | Smith |
Зовнішній запит вибирає student
стовпці рядків, які ми зацікавлені повернути клієнту. Однак у пункті WHERE використовується оператор EXISTS із пов'язаним внутрішнім підзапитом.
Оператор EXISTS повертає true, якщо підзапит повертає щонайменше один запис, а false, якщо не вибрано рядок. Двигун бази даних не повинен повністю виконувати підзапит. Якщо один запис збігається, оператор EXISTS повертає true, і вибирається пов'язаний інший рядок запиту.
Внутрішній підзапит має співвідношення, оскільки стовпець student_id student_grade
таблиці співставляється зі стовпцем id зовнішньої таблиці учнів.
SQL НЕ існує
Розглянемо, що ми хочемо вибрати всіх учнів, які не мають балів нижче 9. Для цього ми можемо використовувати НЕ ІСНУЄТЬСЯ, що заперечує логіку оператора EXISTS.
Таким чином, оператор NOT EXISTS повертає true, якщо в нижньому підзапиті не повертається жодна запис. Однак якщо один запис відповідає внутрішньому підзапиту, оператор NOT EXISTS поверне помилковий, і виконання підзапиту може бути зупинено.
Щоб зіставити всі записи студентів, які не мають асоційованого student_grade зі значенням нижче 9, ми можемо запустити наступний SQL-запит:
SELECT
id, first_name, last_name
FROM
student
WHERE NOT EXISTS (
SELECT 1
FROM
student_grade
WHERE
student_grade.student_id = student.id AND
student_grade.grade < 9
)
ORDER BY id
Запускаючи запит вище, ми бачимо, що відповідає лише запис Alice:
| id | first_name | last_name |
|----|------------|-----------|
| 1 | Alice | Smith |
Отже, перевага використання операторів SQL EXISTS і NOT EXISTS полягає в тому, що внутрішнє виконання підзапиту можна зупинити, доки буде знайдена відповідна запис.