Представьте, что вы объединяете две таблицы: 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 не подойдёт, но иногда это всё, что нужно.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ