Функция ЕстьNull в языке запросов 1С 8.3

· 9 мин

Содержание

Функция ЕСТЬ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. Нужны только те, у кого движения были, — внутреннее соединение, и обёртка не потребуется вовсе.

Проверьте себя
Чему равно выражение `10 + NULL` в запросе 1С?
Какой оператор правильно проверяет поле на NULL?
Что вернёт `ЕСТЬNULL(Остатки.Количество, 0)`, если остаток в регистре есть и равен 5?
Почему не стоит писать `ГДЕ ЕСТЬNULL(Поле, 0) = 0` по индексированному полю?

Частые вопросы

ЕстьNull пишется слитно или через пробел?
Функция — слитно, одним словом: ЕСТЬNULL(Выражение, Значение). А вот оператор проверки — раздельно: ЕСТЬ NULL и ЕСТЬ НЕ NULL. Это разные конструкции.
В чём разница между NULL и пустой ссылкой?
Пустая ссылка — это заполненное значение «ссылка не указана», оно есть в поле. NULL — отсутствие значения, обычно из-за соединения без совпадения. ЕСТЬNULL ловит только NULL; пустую ссылку он не меняет.
Почему условие `Поле = NULL` не находит строки?
Сравнение любого значения с NULL само даёт NULL, а в ГДЕ это трактуется как ложь. Поэтому строк не будет никогда. Используйте Поле ЕСТЬ NULL.
Можно ли подставить в ЕстьNull дату или ссылку?
Да, второй аргумент может быть любого типа. Главное — чтобы он совпадал по типу с первым, иначе колонка станет составного типа. Для даты удобно передавать параметр &ПустаяДата, для ссылки — ЗНАЧЕНИЕ(Справочник.Имя.ПустаяСсылка).
ЕстьNull замедляет запрос?
Сам по себе нет. Замедление возникает, когда функцию ставят вокруг поля в условии соединения или в отборе по индексированному полю — тогда индекс не используется. В выводимых колонках финальной выборки она безопасна.