NULL означает «неизвестно»
Любое другое значение в SQLite — это что-то конкретное: число, строка, блоб. С NULL всё иначе. Это заглушка для значения, которое отсутствует или неизвестно. Эта простая мысль объясняет все странности, которые NULL вытворяет в запросах.
Создадим небольшую таблицу для экспериментов:
Два столбца допускают NULL. У Бориса нет email. У Клео не указан возраст. У Дэна нет ни того, ни другого. Дальше речь пойдёт о том, как корректно запрашивать такие строки и не попасться в ловушку.
Почему = NULL не работает в SQLite
Первое, что хочется написать — WHERE email = NULL. Выглядит логично. И возвращает пустоту:
Ноль строк — хотя у Бориса и Дана почта явно NULL. Дело вот в чём: любое сравнение с NULL даёт в результате NULL, а не true или false. А WHERE в SQLite пропускает только те строки, где условие именно true — NULL под это не подходит, поэтому строка отбрасывается. Вот почему = NULL в SQLite не работает.
Та же ловушка и с <>:
Казалось бы, запрос должен вернуть всех, кроме Ады. А возвращает только Клео. Борис и Дэн, у которых email равен null, выпадают — потому что NULL <> 'ada@example.com' тоже даёт NULL, а не true.
Это самая распространённая засада в SQL. Если запрос внезапно «теряет строки», которых вы там ожидали, — первым делом подозревайте колонку с null.
Используйте IS NULL и IS NOT NULL
Правильный способ проверки на null в SQLite — оператор IS. В отличие от =, он умеет работать с null и всегда возвращает true или false, но никогда не null:
Первый запрос вернёт Бориса и Дэна, второй — Аду и Клео. Операторы IS NULL и IS NOT NULL как раз и созданы для того, чтобы задать вопрос «а это значение вообще есть?». Используйте их везде, где рука так и тянется написать = NULL или <> NULL — это типичная ошибка проверки на NULL в SQLite.
Если же нужно получить «все, кроме Ады, включая записи с неизвестным именем», объедините обе проверки явно:
Теперь в выборку попадают Борис, Клео и Дэн.
NULL заражает арифметику и конкатенацию
Правило «неизвестно» работает не только в сравнениях. Любая операция, в которой замешан NULL, даёт на выходе NULL:
next_year и doubled равны NULL у Клео и Дэна. labelled_age у них тоже NULL — конкатенация строки с NULL даёт NULL, а не 'Возраст: '. Если в колонке может оказаться NULL, а на выходе нужно нормальное значение, это нужно обрабатывать вручную. Как раз для этого пригодятся следующие две функции.
IFNULL: подстановка значения по умолчанию
IFNULL(a, b) возвращает a, если оно не NULL, иначе — b. Это самый простой способ заменить NULL на дефолтное значение:
Борис и Дэн получат (нет email). Клео и Дэн получат 0. Исходные данные при этом не меняются — IFNULL лишь переписывает то, что выводится.
У IFNULL всегда ровно два аргумента. Если запасных вариантов нужно больше — берите COALESCE.
COALESCE: побеждает первое не-NULL значение
COALESCE(a, b, c, ...) перебирает аргументы по порядку и возвращает первый, который не равен NULL. По сути, это IFNULL с любым количеством запасных значений:
Для Ada и Cleo подставляется реальный email. У Boris и Dan поле email равно NULL, поэтому SQLite берёт второй аргумент — адрес, собранный из имени. Если бы и он оказался NULL, сработал бы запасной вариант — 'аноним'.
COALESCE — это переносимый вариант: все крупные СУБД поддерживают его одинаково. IFNULL — это удобное сокращение из SQLite и MySQL, но только для случая с двумя аргументами. По умолчанию используйте COALESCE, а к IFNULL обращайтесь, только когда аргументов действительно два и хочется коротко.
NULL и пустая строка в SQLite — это не одно и то же
Частая путаница: разработчики считают, что NULL и '' взаимозаменяемы. Это не так.
'' — это настоящая строка, просто без символов. А NULL — это вообще отсутствие значения. length('') вернёт 0, тогда как length(NULL) сам по себе будет NULL. И NULL = NULL — это тоже NULL, а вовсе не 1. Именно поэтому в SQLite и существует конструкция IS NULL, а проверка через = NULL не работает.
Если в колонке могут оказаться и '', и NULL, заранее решите, что у вас будет означать «значения нет», и придерживайтесь одного варианта. Иначе в каждом запросе придётся учитывать оба случая — и рано или поздно вы про один из них забудете.
NULL в IN, NOT IN и DISTINCT
Есть ещё пара мест, где NULL подкрадывается незаметно.
IN со списком, в котором затесался NULL, может выдать неожиданный результат — особенно в связке с NOT IN:
Казалось бы, должны вернуться все, у кого возраст не равен 25. Но в ответе — пусто. SQLite разворачивает NOT IN (25, NULL) примерно в age <> 25 AND age <> NULL, а выражение age <> NULL всегда даёт NULL — поэтому всё условие никогда не становится истинным. Лечится это так: нужно отфильтровать NULL либо из самого списка, либо из столбца до сравнения. Именно поэтому = NULL в SQLite не работает — для проверки на NULL всегда используйте IS NULL и IS NOT NULL.
А вот DISTINCT, наоборот, при удалении дубликатов считает NULL-значения равными друг другу:
Получаешь три строки: email Ады, email Клео и одно NULL (сюда схлопнулись Борис и Дэн). С GROUP BY и UNION та же история — они считают все null'ы одной группой, то есть ведут себя ровно наоборот по сравнению с =. В SQL логика тут не везде одинаковая, так что полезно держать в голове, на какой стороне находится каждый оператор.
Короткий чек-лист
- Для проверки на NULL в SQLite используй
IS NULL/IS NOT NULL. Никогда не пиши= NULL. - Любая арифметика, конкатенация или сравнение с участием
NULLвернётNULL. - Чтобы подставить значение по умолчанию, бери
COALESCE(a, b, c, ...). Для случая с двумя аргументами есть короткая форма —IFNULL(a, b). - Пустая строка
''— это неNULL. Для каждой колонки выбери что-то одно как обозначение «нет значения». NOT IN (..., NULL)— почти всегда баг. Сначала выкидывай null'ы из списка.
Дальше: сортировка результатов
Когда фильтрация строк под контролем — включая строки с null, — пора заняться порядком вывода. На следующей странице разберём ORDER BY, и у него тоже есть свои взгляды на то, где должны оказаться null'ы в отсортированной выдаче.
Часто задаваемые вопросы
Почему column = NULL в SQLite ничего не возвращает?
column = NULL в SQLite ничего не возвращает?Потому что NULL означает «значение неизвестно», а сравнение с неизвестным даёт не TRUE, а тоже неизвестный результат. В итоге WHERE col = NULL не находит ни одной строки — даже тех, где значение реально пустое. Правильный вариант — WHERE col IS NULL. С <> та же история: пишите IS NOT NULL.
Чем IFNULL отличается от COALESCE в SQLite?
IFNULL(a, b) принимает строго два аргумента: возвращает a, а если он NULL — то b. COALESCE(a, b, c, ...) берёт сколько угодно аргументов и возвращает первый не-NULL. То есть IFNULL — это укороченная версия для двух значений, а COALESCE — общий случай и при этом стандартная функция, которая работает почти во всех СУБД.
NULL и пустая строка — это одно и то же в SQLite?
Нет. NULL — это «значения нет вообще», а '' — это вполне конкретная строка нулевой длины. Проверка '' IS NULL вернёт 0 (ложь), length('') равно 0, а length(NULL) сам по себе NULL. Если в столбце могут встречаться оба варианта, придётся либо обрабатывать их отдельно в запросах, либо приводить к одному виду на этапе записи.