Menu

SQLite и NULL: IS NULL, COALESCE, IFNULL на практике

Разбираемся, как операторы SQLite ведут себя с NULL: почему = и <> работают не так, как кажется, и зачем нужны IS NULL, IS NOT NULL, COALESCE и IFNULL.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

NULL означает «неизвестно»

Любое другое значение в SQLite — это что-то конкретное: число, строка, блоб. С NULL всё иначе. Это заглушка для значения, которое отсутствует или неизвестно. Эта простая мысль объясняет все странности, которые NULL вытворяет в запросах.

Создадим небольшую таблицу для экспериментов:

Два столбца допускают NULL. У Бориса нет email. У Клео не указан возраст. У Дэна нет ни того, ни другого. Дальше речь пойдёт о том, как корректно запрашивать такие строки и не попасться в ловушку.

Почему = NULL не работает в SQLite

Первое, что хочется написать — WHERE email = NULL. Выглядит логично. И возвращает пустоту:

Ноль строк — хотя у Бориса и Дана почта явно NULL. Дело вот в чём: любое сравнение с NULL даёт в результате NULL, а не true или false. А WHERE в SQLite пропускает только те строки, где условие именно trueNULL под это не подходит, поэтому строка отбрасывается. Вот почему = 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 ничего не возвращает?

Потому что 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. Если в столбце могут встречаться оба варианта, придётся либо обрабатывать их отдельно в запросах, либо приводить к одному виду на этапе записи.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ