Уяви, що ти об'єднуєш дві таблиці: students (студенти) і enrollments (записи на курси). Якщо в таблиці enrollments немає інформації про якогось студента, але ти використовуєш, наприклад, LEFT JOIN, рядки з таблиці students все одно з'являться, але інформація з enrollments буде відсутня. Замість конкретних даних у таких випадках з'являється NULL.
Приблизно так це виглядає:
Таблиця students:
| id | name |
|---|---|
| 1 | Eva |
| 2 | Peter |
| 3 | Anna |
Таблиця enrollments:
| student_id | course_name |
|---|---|
| 1 | Математика |
| 1 | Інформатика |
| 2 | Фізика |
Запит з LEFT JOIN:
SELECT students.id, students.name, enrollments.course_name
FROM students
LEFT JOIN enrollments ON students.id = enrollments.student_id;
Результат:
| id | name | course_name |
|---|---|---|
| 1 | Eva | Математика |
| 1 | Eva | Інформатика |
| 2 | Peter | Фізика |
| 3 | Anna | NULL |
Ну, привіт, NULL! Як бачиш, для Анни, яка не записана ні на один курс, інформація про курс відсутня, і замість чогось виводиться NULL.
Як NULL впливає на запити?
NULL — це не "нуль" і не "порожній рядок", це відсутність значення. Таке поводження має кілька цікавих (і іноді дратівливих) наслідків:
Порівняння з NULL:
Якщо ти напишеш щось типу WHERE course_name = NULL, запит не поверне рядків з NULL. Чому? Бо з NULL не можна порівнювати значення напряму.
Щоб перевірити, чи є NULL, треба використовувати спеціальні оператори:
WHERE course_name IS NULL
Математичні операції:
Будь-яка операція з NULL повертає NULL. Наприклад:
SELECT 5 + NULL; -- результат: NULL
Агрегатні функції:
Більшість агрегатних функцій, таких як SUM(), AVG(), ігнорують NULL, але COUNT(*) рахує їх як "існуючі рядки".
Як боротися з NULL?
- Заміна
NULLна зрозумілі значення за допомогоюCOALESCE()
Функція COALESCE() дозволяє замінити NULL іншим значенням. Наприклад, якщо курс відсутній, можна вказати "Немає курсу":
SELECT
students.id,
students.name,
COALESCE(enrollments.course_name, 'Немає курсу') AS course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id;
Результат:
| id | name | course_name |
|---|---|---|
| 1 | Eva | Математика |
| 1 | Eva | Інформатика |
| 2 | Peter | Фізика |
| 3 | Anna | Немає курсу |
Тепер виглядає набагато краще, правда ж?
- Фільтрація
NULLзначень
Якщо ти не хочеш бачити рядки з NULL, можна використати умову WHERE ... IS NOT NULL. Наприклад:
SELECT
students.id,
students.name,
enrollments.course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id
WHERE
enrollments.course_name IS NOT NULL;
Результат:
| id | name | course_name |
|---|---|---|
| 1 | Eva | Математика |
| 1 | Eva | Інформатика |
| 2 | Peter | Фізика |
Анна зникає з результату, бо у неї немає записів на курси.
- Рахуємо з урахуванням
NULL: приклад зCOUNT
Як згадувалося раніше, деякі функції ігнорують NULL, а деякі — ні. Наприклад:
Щоб порахувати всі рядки, включаючи ті, де є NULL:
SELECT COUNT(*) FROM students; -- Рахує ВСІ рядки (включаючи ті, де `course_name` = NULL)
Щоб порахувати тільки рядки, де немає NULL:
SELECT COUNT(course_name) FROM enrollments;
- Умовні вирази з
CASE
Якщо тобі не подобається COALESCE() або хочеш більше гнучкості, спробуй використати CASE. Наприклад:
SELECT
students.id,
students.name,
CASE
WHEN enrollments.course_name IS NULL THEN 'Немає курсу'
ELSE enrollments.course_name
END AS course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id;
Результат буде таким самим, як при використанні COALESCE(), але CASE дозволяє описувати більш складні правила.
- Використовуй
INNER JOIN, якщо впевнений у відсутностіNULL
Найрадикальніший спосіб уникнути NULL — взагалі не допускати їх появи, використовуючи INNER JOIN. Цей тип об'єднання повертає тільки рядки з відповідниками в обох таблицях:
SELECT
students.id,
students.name,
enrollments.course_name
FROM
students INNER JOIN enrollments
ON students.id = enrollments.student_id;
Без сюрпризів — тільки студенти, записані на курси.
Результат:
| id | name | course_name |
|---|---|---|
| 1 | Eva | Математика |
| 1 | Eva | Інформатика |
| 2 | Peter | Фізика |
Якщо твої дані вимагають відображення всіх значень, включаючи NULL, INNER JOIN не підійде, але іноді це саме те, що треба.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ