NULL в SQL означает, что значения в ячейке нет: его не знают или оно к этой строке не относится. Пустая строка '' устроена иначе. Это настоящее значение, в котором ноль символов.
Отсюда главное правило: NULL ищут через IS NULL, знак равенства тут не работает. Запрос WHERE phone = NULL не вернёт ни одной строки, даже если пустых телефонов в таблице сотни. Если вы только начинаете писать запросы, сначала загляните в обзор языка SQL и того, зачем его учить.
Все примеры ниже мы запускали на MySQL 8.0 с настройками по умолчанию. Вывод в блоках кода скопирован из консоли без правок.
Что значит NULL в базе данных

В стандарте SQL значение NULL читается как «неизвестно». Поэтому сравнение с ним возвращает третий логический результат, UNKNOWN: ни «да», ни «нет». Так логику описывает справка Microsoft о NULL и UNKNOWN в Transact-SQL. MySQL рассуждает так же, только в выводе показывает этот третий результат как NULL.
Сравните три похожих на вид значения:
| Что в ячейке | Что это значит | Пример в анкете клиента | Как найти |
|---|---|---|---|
NULL |
значения нет или оно неизвестно | телефон у клиента не спрашивали | IS NULL |
'' |
строка есть, но в ней ноль символов | отчества нет, поле оставили пустым | = '' |
0 |
обычное число | на бонусном счёте ноль баллов | = 0 |
Для отчёта это разные факты. «Телефон не спрашивали» и «отчества нет» нельзя смешивать, и база хранит их по-разному.
NULL как тип данных
Отдельного типа под NULL в MySQL нет. Это значение, которое может лежать в столбце любого типа: INT, VARCHAR, DATE. Запрещает его только пометка NOT NULL в описании столбца, а без неё столбец по умолчанию принимает NULL.
Такие вопросы часто задают на первом техническом интервью, наравне с JOIN и группировкой. Чем ещё проверяют новичков, видно по разбору собеседования на аналитика данных.
Что значит null в программировании
Слово null есть почти в каждом языке. Везде оно значит «здесь ничего нет», но детали различаются:
- JavaScript.
nullпрограммист ставит сам, чтобы явно показать пустоту.undefinedозначает, что переменной ещё ничего не присвоили. Описание есть в глоссарии MDN. Сюрприз для новичков:typeof nullвозвращает"object", аnull == undefinedдаётtrue. - Python. Вместо null здесь объект
None, и проверяют его операторомis:if value is None. Почему здесь нужен именноis, объясняет статья про операторы в Python. - Java и C#. null означает пустую ссылку: переменная никуда не указывает. Вызов метода через такую ссылку в Java падает с ошибкой
NullPointerException.
Когда программа читает строку из базы, NULL превращается в null своего языка. В Python это прямо записано в стандарте драйверов баз данных PEP 249: NULL из SQL приходит как None. Пустая строка при этом так и остаётся пустой строкой.
Проверка
if not valueв Python пропускает иNone, и''. Это удобно, но разница между «не спрашивали» и «пусто» в этот момент теряется.
Если вы пишете фронтенд, тему null и undefined стоит закрыть сразу, она всплывает с первых задач. С чего начать JavaScript, мы разбирали в статье о том, зачем новичку учить JavaScript.
Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединитьсяЧем NULL отличается от пустой строки в MySQL
Начнём со сравнения. Знак = с NULL всегда даёт NULL, даже если сравнить NULL с самим собой:
SELECT NULL = NULL, NULL IS NULL, NULL <=> NULL, '' = '', '' IS NULL;
+-------------+--------------+---------------+---------+------------+
| NULL = NULL | NULL IS NULL | NULL <=> NULL | '' = '' | '' IS NULL |
+-------------+--------------+---------------+---------+------------+
| NULL | 1 | 1 | 1 | 0 |
+-------------+--------------+---------------+---------+------------+
Оператор <=> в MySQL называется NULL-безопасным равенством: два NULL для него равны. Остальные различия видно на функциях и индексах.
| Что проверяем | NULL | Пустая строка '' |
|---|---|---|
LENGTH() |
NULL | 0 |
CONCAT('Иван', …) |
NULL, пропадает весь результат | «Иван» |
COUNT(столбец) |
не считается | считается |
| уникальный индекс | повторов сколько угодно | второе '' даёт ошибку |
ORDER BY … ASC |
в самом начале | сразу после NULL, перед любым текстом |
GROUP BY |
все NULL в одной группе | все '' в одной группе |
Строку с уникальным индексом стоит запомнить отдельно. Мы создали таблицу contacts с уникальным полем email и записали туда по два одинаковых значения:
INSERT INTO contacts (email) VALUES (NULL), (NULL); -- обе строки записаны
INSERT INTO contacts (email) VALUES (''), ('');
ERROR 1062 (23000): Duplicate entry '' for key 'contacts.uq_email'
Поэтому необязательное уникальное поле хранят как NULL. Пустая строка займёт место в индексе, и второй клиент без почты уже не сохранится. Индексы и ограничения подробно проходят те, кто идёт в администраторы баз данных: это основа их работы.
Порядок сортировки и группировку MySQL описывает в разделе Working with NULL Values. Сайт документации dev.mysql.com из России сейчас открывается страницей с ошибкой, поэтому все правила выше мы проверили запросами. Русский перевод тех же правил есть в старом руководстве на mysql.ru.
Запросы для поиска пустых значений
Возьмём учебную таблицу users. У Бориса и Глеба телефон NULL, у Веры пустая строка:
SELECT id, name, phone FROM users ORDER BY phone, id;
+----+-------+--------------+
| id | name | phone |
+----+-------+--------------+
| 2 | Борис | NULL |
| 4 | Глеб | NULL |
| 3 | Вера | |
| 1 | Анна | +79001112233 |
+----+-------+--------------+
Четыре запроса закрывают почти все задачи. Если таких запросов в работе становится всё больше, загляните в подборку курсов по SQL.
-- только NULL: Борис и Глеб
SELECT id, name FROM users WHERE phone IS NULL;
-- только пустые строки: Вера
SELECT id, name FROM users WHERE phone = '';
-- и то и другое: Борис, Вера, Глеб
SELECT id, name FROM users WHERE phone IS NULL OR phone = '';
-- то же короче: NULLIF превращает '' в NULL
SELECT id, name FROM users WHERE NULLIF(phone, '') IS NULL;
Чтобы заменить NULL на понятный текст или ноль при выводе, есть три функции:
IFNULL(x, y)возвращаетy, еслиxравен NULL. Работает в MySQL, принимает два аргумента.COALESCE(x, y, z…)возвращает первый аргумент, который не NULL. Это стандарт SQL, он есть и в PostgreSQL, и в SQL Server.NULLIF(a, b)возвращает NULL, еслиaиbравны. Удобна, чтобы пустую строку считать отсутствием значения.
SELECT name,
COALESCE(NULLIF(phone, ''), 'нет телефона') AS phone,
IFNULL(bonus, 0) AS bonus
FROM users;
+-------+--------------+-------+
| name | phone | bonus |
+-------+--------------+-------+
| Анна | +79001112233 | 100 |
| Борис | нет телефона | 0 |
| Вера | нет телефона | 50 |
| Глеб | нет телефона | 0 |
+-------+--------------+-------+
Без NULLIF Вера осталась бы с пустым телефоном. COALESCE('', 'замена') и IFNULL('', 'замена') возвращают пустую строку: для них '' полноценное значение.
Запросы удобно гонять в DBeaver на копии базы. Как его подключить, описано в инструкции по подключению к базе в DBeaver. В PostgreSQL вместо <=> пишут IS NOT DISTINCT FROM, синтаксис есть в русской документации Postgres Pro.
Подводные камни в запросах

Ошибки с NULL не выдают сообщений. Запрос отрабатывает, просто в результате не хватает строк или цифра получается другая. Вот пять случаев, которые встречаются чаще всего.
«Не равно» теряет строки с NULL
Хотим всех, кто не из Москвы. У Веры город не указан, и она пропадает из результата:
SELECT id, name, city FROM users WHERE city <> 'Москва';
+----+-------+--------+
| id | name | city |
+----+-------+--------+
| 2 | Борис | Казань |
+----+-------+--------+
Решение: добавить OR city IS NULL или написать WHERE NOT (city <=> 'Москва'). Оба варианта вернут Бориса и Веру.
NOT IN с NULL в списке возвращает пустоту
В таблице orders два заказа: один от Анны, у второго user_id не заполнен. Ищем тех, кто ничего не заказывал:
SELECT name FROM users WHERE id NOT IN (SELECT user_id FROM orders);
Empty set
Ни одной строки, хотя Борис, Вера и Глеб заказов не делали. Для MySQL «id не равен NULL» означает «неизвестно», поэтому ни одна строка не проходит фильтр.
Решение: NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id) или условие WHERE user_id IS NOT NULL внутри подзапроса. Оба вернут троих.
Арифметика и склейка строк превращаются в NULL
bonus + 10у Бориса и Глеба даёт NULL. Ожидаемых 10 баллов не будет.CONCAT('Иван', NULL)возвращает NULL целиком.CONCAT_WS(' ', 'Иван', NULL, 'Петров')пропускает NULL и выдаёт «Иван Петров».
Решение: оборачивать столбцы в IFNULL до вычислений, а ФИО собирать через CONCAT_WS.
AVG делит не на то число
SELECT COUNT(*), COUNT(bonus), SUM(bonus), AVG(bonus), AVG(IFNULL(bonus, 0)) FROM users;
+----------+--------------+------------+------------+-----------------------+
| COUNT(*) | COUNT(bonus) | SUM(bonus) | AVG(bonus) | AVG(IFNULL(bonus, 0)) |
+----------+--------------+------------+------------+-----------------------+
| 4 | 2 | 150 | 75.0000 | 37.5000 |
+----------+--------------+------------+------------+-----------------------+
Агрегатные функции пропускают NULL. AVG(bonus) делит 150 только на двух клиентов с бонусами. Если NULL у вас значит «ноль баллов», правильный ответ 37,5, и получить его можно только через IFNULL.
LEFT JOIN превращается в обычный
Условие на правую таблицу в WHERE отсекает строки, где LEFT JOIN подставил NULL. Такие условия переносят в ON. Пример с заказами разобран в статье о задачах по SQL на собеседовании.
Пустая строка, NOT NULL и строгий режим
MySQL 8.0 по умолчанию работает в строгом режиме STRICT_TRANS_TABLES. В нём пустую строку нельзя записать в числовой столбец, а NULL нельзя записать в столбец с NOT NULL:
INSERT INTO t (amount, note) VALUES ('', 'тест');
ERROR 1366 (HY000): Incorrect integer value: '' for column 'amount' at row 1
INSERT INTO t (amount, note) VALUES (5, NULL);
ERROR 1048 (23000): Column 'note' cannot be null
Если строгий режим выключен, картина другая. Тогда '' в числовом столбце молча превращается в 0, и MySQL выдаёт только предупреждение. Мы проверили это, сбросив sql_mode в сессии: в таблицу записался ноль. Какие флаги за что отвечают, расписано в разделе Server SQL Modes (тот же сайт dev.mysql.com, из России открывается с ошибкой).
Ещё один нюанс касается пробелов. Строка из трёх пробелов равна пустой строке или нет, зависит от сопоставления (collation) столбца:
| Сопоставление | Результат ' ' = '' |
|---|---|
utf8mb4_0900_ai_ci, по умолчанию в MySQL 8.0 |
0, пробелы считаются символами |
utf8mb4_unicode_ci, встречается в базах, созданных до MySQL 8.0 |
1, пробелы в конце игнорируются |
Если поле заполняют люди через форму, сравнивайте TRIM(поле) = ''. Так запрос одинаково сработает на любом сопоставлении.
Что хранить в пустом поле при проектировании таблицы

Ориентиры такие:
| Ситуация | Что хранить | Почему |
|---|---|---|
| значение неизвестно: телефон, дата увольнения, рейтинг | NULL | явная проверка IS NULL, честные COUNT и AVG |
| необязательное уникальное поле: почта, ИНН | NULL | уникальный индекс пропускает повторы NULL |
| внешний ключ без связи | NULL | пустую строку база будет искать в родительской таблице и не найдёт |
| числа и даты | NULL | в строгом режиме '' туда не запишется |
| текст, где «пусто» и «не спрашивали» для вас одно и то же | '' и NOT NULL DEFAULT '' |
одна проверка = '' вместо двух |
Главное, одна договорённость на столбец. Проблемы начинаются, когда в одном поле живут и NULL, и пустые строки: тогда каждый отчёт обязан проверять оба варианта, и рано или поздно кто-то забудет. Если так уже вышло, приведите поле к одному виду. Команда UPDATE users SET phone = NULL WHERE phone = '' в нашей таблице перевела телефон Веры в NULL, и IS NULL стал находить всех троих.
Отдельно об Oracle. Там пустая строка и NULL одно и то же: документация Oracle прямо пишет, что значение нулевой длины считается null, и советует не полагаться на это в будущем. При переносе базы из Oracle в MySQL или PostgreSQL проверки на IS NULL перестают ловить пустые строки, их надо пересмотреть.
В PostgreSQL с 15-й версии уникальный индекс можно объявить как UNIQUE NULLS NOT DISTINCT. Тогда второй NULL тоже даст ошибку дубликата, как пустая строка в MySQL. Подробности в разделе об ограничениях документации Postgres Pro.
Такие решения в команде обычно принимает тот, кто отвечает за структуру базы. Чем он занят каждый день, рассказали в статье о том, кто такой администратор баз данных.
Где освоить SQL системно
С NULL можно разобраться за вечер, а вот уверенно писать запросы с JOIN, группировками и подзапросами учатся неделями практики на живых данных. Если SQL нужен для работы аналитиком или разработчиком, удобнее пройти курс с задачами и проверкой.
Ниже курсы из нашего каталога, все программы собраны в разделе курсов по SQL. Как выстроить обучение по месяцам, расписано в плане как стать SQL-разработчиком, а чем занят аналитик на SQL, мы разбирали в статье о профессии SQL-аналитика.
| Курс | Школа | Стоимость со скидкой | В рассрочку | Длительность | Обзор курса от Checkroi |
|---|---|---|---|---|---|
| Нейросети для рабочих задач Перейти на сайт курса | 31 290 ₽ | - | 1 месяц | Обзор курса | |
| SQL-разработчик: тариф PRO Перейти на сайт курса | 89 900 ₽ | 7491 ₽/мес. | 2 месяца | Обзор курса | |
| SQL-разработчик: тариф Базовый Перейти на сайт курса | 79 900 ₽ | 3329 ₽/мес. | 3 месяца | Обзор курса | |
| SQL для анализа данных Перейти на сайт курса | 50 828 ₽ | 6203 ₽/мес. | 2 месяца | Обзор курса | |
| SQL с нуля | 134 640 ₽ | 5500 ₽/мес. | 12 месяцев | Обзор курса |
Больше программ — в полном каталоге курсов по SQL




