Функция ЕстьNull в языке запросов 1С 8.3
Содержание
Функция ЕСТЬNULL(Выражение, ЗначениеЕслиNull) возвращает первый аргумент, а если он равен NULL — подставляет второй. Пишется одним словом, английский аналог — ISNULL(,). Чаще всего её ставят вокруг числовых полей из левых соединений, чтобы вместо пустоты получить ноль: ЕСТЬNULL(Остатки.КоличествоОстаток, 0).
NULL — это не ноль и не пустая ссылка. Это «значения нет вообще»: поле не вернулось из соединения, нет записи в регистре, не заполнена часть составного типа. С таким значением не работает
арифметика и не срабатывают обычные сравнения, поэтому ЕСТЬNULL и нужен — он превращает «ничего» в нормальное значение нужного типа.
Разберём, откуда NULL появляется, как правильно подставлять значение по умолчанию, чем функция отличается от ВЫБОР КОГДА и где она незаметно тормозит запрос.
Откуда в запросе берётся NULL и чем он отличается от пустого
NULL появляется в запросе 1С в нескольких ситуациях:
- 🔗 Левое, правое или полное соединение — для строк, которым не нашлось пары, все поля присоединённой таблицы равны NULL.
- 📊 Виртуальные таблицы регистров — при соединении с остатками/оборотами по позиции, у которой движений не было.
- 🧩 Незаполненная часть составного типа — реквизит типа «Справочник ссылка; Документ ссылка», у которого заполнена не та ветка.
- ➗ Деление и выражения, где один из операндов уже NULL.
- 🔀 Объединение запросов через
ОБЪЕДИНИТЬ ВСЕ, когда в одной части поле заполнено, а в другой стоит заглушкаNULL КАК Поле.
Классический пример — список номенклатуры с остатками:
ВЫБРАТЬ
Номенклатура.Наименование,
Остатки.КоличествоОстаток
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Остатки.Номенклатура = Номенклатура.Ссылка
У позиций без остатка КоличествоОстаток будет не 0, а NULL.
Главное: NULL — это не то же самое, что пустая ссылка, пустая дата, пустая строка или число 0. Пустая ссылка — это конкретное значение «ссылка не указана». NULL — отсутствие значения как такового. Поэтому ЕСТЬNULL ловит только NULL и не трогает пустые ссылки и нули — для них он бесполезен.
Быстрый способ проверить, где в выборке NULL, — временно вывести поле как есть и посмотреть на пустые ячейки в консоли запросов. Пустая ячейка без значения — это и есть NULL.
Синтаксис ЕстьNull и подстановка значения по умолчанию
Функция принимает ровно два аргумента:
ЕСТЬNULL(<Выражение>, <ЗначениеЕслиNull>)
- Если
<Выражение>не NULL — возвращается оно само. - Если NULL — возвращается
<ЗначениеЕслиNull>.
Исправим предыдущий запрос:
ВЫБРАТЬ
Номенклатура.Наименование,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Остатки.Номенклатура = Номенклатура.Ссылка
Теперь у позиций без движений будет честный ноль.
Значение по умолчанию подбирают под тип поля, иначе получится неожиданный результат:
| Тип поля | Что подставлять |
|---|---|
| Число | 0 |
| Строка | "" |
| Дата | ДАТАВРЕМЯ(1, 1, 1) или параметр &ПустаяДата |
| Булево | ЛОЖЬ |
| Ссылка | ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка) |
Тип результата ЕСТЬNULL — это объединение типов обоих аргументов. Если подставить в числовое поле строку "нет", колонка станет составного типа «Число; Строка», и дальше по ней не получится ни суммировать, ни сортировать как по числу. Тип замены должен совпадать с типом выражения.
Функции можно вкладывать друг в друга. Каскад из ЕСТЬNULL берёт первое непустое значение из нескольких источников:
ЕСТЬNULL(ЦенаПрайс.Цена, ЕСТЬNULL(ЦенаДоговор.Цена, 0)) КАК Цена
Сначала проверяется цена из прайса; если её нет — цена из договора; если и её нет — ноль. Такой приём удобен, когда данные могут прийти из разных таблиц по приоритету.
ЕстьNull или ВЫБОР КОГДА: что выбрать
Ту же подстановку можно записать через ВЫБОР:
ВЫБОР
КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL
ТОГДА 0
ИНАЧЕ Остатки.КоличествоОстаток
КОНЕЦ
Результат одинаковый, но разница есть:
ЕСТЬNULLкороче и читается сразу — это его единственная задача: заменить NULL.ВЫБОР КОГДАуниверсальнее: он проверяет любое условие, а не только «равно ли NULL». Если нужно заменить и NULL, и ноль, и отрицательное — безВЫБОРне обойтись.- В
ВЫБОР КОГДА ... ИНАЧЕвыражение упоминается дважды. Для простой замены NULL это лишнее дублирование.
Правило простое: нужна только замена NULL — берите ЕСТЬNULL. Нужна логика сложнее (несколько условий, замена по диапазону) — ВЫБОР КОГДА.
Обратите внимание на проверку самого NULL. Оператор сравнения здесь — ЕСТЬ NULL и ЕСТЬ НЕ NULL, два отдельных слова:
ГДЕ Остатки.КоличествоОстаток ЕСТЬ NULL // строки без остатка
ГДЕ Остатки.КоличествоОстаток ЕСТЬ НЕ NULL // строки, где остаток есть
Как NULL ломает суммы и условия — и как это чинить
Три ошибки встречаются чаще всего. Все — из-за того, что NULL ведёт себя не как ноль.
1. NULL съедает арифметику. Любая операция с NULL даёт NULL: 5 + NULL равно NULL, а не 5. Если складывать два поля из левых соединений без обёртки, пустое слагаемое обнулит весь результат:
// неверно: при отсутствии прихода или расхода Сальдо = NULL
Приход.Сумма - Расход.Сумма КАК Сальдо
// верно
ЕСТЬNULL(Приход.Сумма, 0) - ЕСТЬNULL(Расход.Сумма, 0) КАК Сальдо
2. Сравнение = NULL никогда не истинно. Запись ГДЕ Поле = NULL не вернёт ни одной строки — результат сравнения с NULL сам равен NULL, а в условии ГДЕ это считается ложью.
// неверно — всегда пусто
ГДЕ Остатки.КоличествоОстаток = NULL
// верно
ГДЕ Остатки.КоличествоОстаток ЕСТЬ NULL
Запомните: NULL нельзя сравнивать через =, <>, >. Только ЕСТЬ NULL / ЕСТЬ НЕ NULL.
3. Неверный тип значения по умолчанию. Подстановка значения чужого типа превращает колонку в составной тип и ломает дальнейшие группировки и условия. Следите, чтобы второй аргумент совпадал по типу с первым.
Отдельный нюанс по агрегатам: СУММА и СРЕДНЕЕ сами игнорируют NULL при подсчёте, поэтому в итоговой сумме NULL не искажает результат. Но в выводимой колонке по строке без данных останется пустая ячейка — для отображения значение всё равно лучше обернуть в ЕСТЬNULL.
Производительность: где ЕстьNull замедляет запрос
Сам по себе ЕСТЬNULL дешёвый. Проблемы начинаются, когда его ставят не туда.
- 🚫 Не оборачивайте поле в условии соединения
ПОи вГДЕпо индексированному полю. Функция вокруг поля мешает оптимизатору использовать индекс — запрос начинает читать таблицу целиком. - ✅ Подставляйте значение в финальной выборке, на уже отобранных и свёрнутых данных, а не внутри отборов.
- 🧮 Следите за составным типом. Если
ЕСТЬNULLвозвращает ссылку и типы расходятся, платформа может делать лишние обращения к таблицам. Ограничьте тип черезВЫРАЗИТЬ(... КАК Справочник.Контрагенты).
Сравните два подхода к отбору позиций без остатка:
// медленно: функция по полю мешает индексу
ГДЕ ЕСТЬNULL(Остатки.КоличествоОстаток, 0) = 0
// быстро: прямая проверка на NULL
ГДЕ Остатки.КоличествоОстаток ЕСТЬ NULL
Второй вариант не только короче, но и даёт оптимизатору работать с индексом.
Если по полю с подставленным значением всё же нужно фильтровать или соединять, вынесите предварительный запрос во временную таблицу: обработайте NULL в ней один раз, проиндексируйте нужное поле, а затем соединяйтесь с уже готовыми данными. Так подстановка выполняется однократно, а не на каждой строке соединения.
ЕстьNull в соединениях, подзапросах и итогах
В реальных запросах ЕСТЬNULL обычно стоит не в одном месте, а вокруг каждого числового поля из присоединённых таблиц.
Несколько левых соединений. Когда к основному списку цепляются остатки, обороты и цены, оборачивайте каждую меру отдельно:
ВЫБРАТЬ
Товары.Ссылка,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток,
ЕСТЬNULL(Цены.Цена, 0) КАК Цена,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) * ЕСТЬNULL(Цены.Цена, 0) КАК Сумма
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Остатки.Номенклатура = Товары.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
ПО Цены.Номенклатура = Товары.Ссылка
Без обёртки в поле Сумма любой NULL обнулит произведение.
Подзапросы. Если поле берётся из вложенного запроса, где строки может не быть, результат снаружи придёт как NULL — его тоже оборачивают на верхнем уровне.
Сбор мер из двух регистров. Когда нужно свести в одну таблицу показатели из разных источников — например, продажи и возвраты из двух регистров, — их объединяют через ОБЪЕДИНИТЬ ВСЕ, заполняя недостающие колонки заглушкой 0 КАК Поле, а затем группируют. Если в части запроса источник может дать NULL, оборачивайте его перед группировкой:
ВЫБРАТЬ
Номенклатура,
СУММА(ЕСТЬNULL(Продажи, 0)) КАК Продано,
СУММА(ЕСТЬNULL(Возвраты, 0)) КАК Возвращено
ИЗ (
ВЫБРАТЬ Номенклатура, Количество КАК Продажи, 0 КАК Возвраты
ИЗ РегистрНакопления.Продажи
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ Номенклатура, 0, Количество
ИЗ РегистрНакопления.Возвраты
) КАК Данные
СГРУППИРОВАТЬ ПО Номенклатура
Итоги и группировки. В разделе ИТОГИ ... ПО агрегаты игнорируют NULL, но по строкам детализации пустые ячейки останутся. Для аккуратной таблицы подставляйте значение в самой выборке, а не после.
Составной тип и ВЫРАЗИТЬ. Если ЕСТЬNULL возвращает ссылку, а типы первого и второго аргументов различаются, итоговое поле становится составного типа. Дальнейшие соединения по такому полю работают медленнее. Ограничьте тип явно: ЕСТЬNULL(ВЫРАЗИТЬ(Поле КАК Справочник.Контрагенты), ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)).
Не оборачивайте в ЕСТЬNULL ключ соединения внутри ПО ради «совпадения NULL с NULL» — это и замедляет запрос, и обычно означает ошибку в логике связи таблиц. Соединяйте по реальным ключам, а подстановку делайте в выводимых полях.
Когда ЕстьNull не нужен: соединение и виртуальные таблицы
ЕСТЬNULL лечит следствие, а не причину. Иногда NULL проще не создавать.
- Внутреннее соединение вместо левого. Если строки без пары вам не нужны, замените
ЛЕВОЕ СОЕДИНЕНИЕнаВНУТРЕННЕЕ СОЕДИНЕНИЕ— несовпавшие строки отсеются, NULL не появится, обёртка не понадобится. - Параметры виртуальных таблиц. Отбор лучше задавать в параметрах виртуальной таблицы остатков (период, условие), а не присоединять весь регистр и потом фильтровать. Данных на входе меньше — и NULL, и лишних строк тоже.
- ВЫБОР КОГДА при сложной логике. Когда замена зависит от нескольких условий,
ЕСТЬNULLуже не помогает — нуженВЫБОР КОГДА.
Выбор между «обернуть в ЕСТЬNULL» и «убрать NULL соединением» зависит от того, нужны ли вам строки без данных. Нужны все позиции, даже с нулевым остатком, — левое соединение плюс ЕСТЬNULL. Нужны только те, у кого движения были, — внутреннее соединение, и обёртка не потребуется вовсе.
Частые вопросы
ЕстьNull пишется слитно или через пробел?
ЕСТЬNULL(Выражение, Значение). А вот оператор проверки — раздельно: ЕСТЬ NULL и ЕСТЬ НЕ NULL. Это разные конструкции.В чём разница между NULL и пустой ссылкой?
ЕСТЬNULL ловит только NULL; пустую ссылку он не меняет.Почему условие `Поле = NULL` не находит строки?
ГДЕ это трактуется как ложь. Поэтому строк не будет никогда. Используйте Поле ЕСТЬ NULL.Можно ли подставить в ЕстьNull дату или ссылку?
&ПустаяДата, для ссылки — ЗНАЧЕНИЕ(Справочник.Имя.ПустаяСсылка).