Как в запросе 1С отловить и заменить NULL

· 9 мин

Содержание

Проверить поле на NULL в запросе 1С можно одним из двух инструментов: оператором ЕСТЬ NULL в условии ГДЕ или ВЫБОР, когда надо отобрать или отметить пустые строки, и функцией ЕСТЬNULL(Поле, Значение), когда NULL надо сразу заменить на число или строку. Сравнение Поле = NULL не работает — такое условие всегда ложно, строки молча теряются.

Ниже — где в запросе вообще появляется NULL, чем он отличается от Неопределено и пустой строки, как корректно фильтровать и заменять пустые значения в соединениях, временных таблицах и СКД, и какие из этих приёмов бьют по производительности.

Откуда в запросе берётся NULL и чем он отличается от «Неопределено»

NULL — это не значение, а его отсутствие в результате запроса. Платформа подставляет NULL в трёх типичных ситуациях:

  • 🔗 Левое или правое соединение без пары. ЛЕВОЕ СОЕДИНЕНИЕ возвращает все строки левой таблицы; для тех, у кого в правой нет совпадения, поля правой таблицы будут NULL.
  • 📊 Виртуальные таблицы остатков и оборотов. Если по номенклатуре нет движений, её ресурс (КоличествоОстаток, СуммаОборот) приходит как NULL, а не как 0.
  • 🧩 Реквизит составного типа. Обращение к реквизиту через точку у ссылки «не того» типа (например, Регистратор.Контрагент, когда регистратор — не тот документ) даёт NULL.

Главное, что путает новичков: NULL в запросе и Неопределено во встроенном языке — это разные вещи. NULL живёт внутри запроса и означает «в базе такого значения нет». Неопределено — это платформенное значение, которое, в частности, получается, когда пустая ссылка приводится к типу. В выборке результата запроса поле с NULL при чтении через Выборка.Поле превращается в NULL — отдельное значение, а не в Неопределено и не в 0.

Отличать стоит и от пустой ссылки: пустая ссылка справочника — это заполненное значение «пустая ссылка», оно не NULL. Поэтому Поле ЕСТЬ NULL и Поле = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка) — две разные проверки.

Оператор ЕСТЬ NULL: правильная проверка в условии

Для отбора или проверки пустых строк используют оператор ЕСТЬ NULL и его отрицание ЕСТЬ НЕ NULL. Это единственный корректный способ сравнить значение с NULL.

ВЫБРАТЬ
    Номенклатура.Наименование КАК Наименование,
    Остатки.КоличествоОстаток КАК Количество
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
    ПО Остатки.Номенклатура = Номенклатура.Ссылка
ГДЕ
    Остатки.КоличествоОстаток ЕСТЬ NULL

Этот запрос вернёт позиции, по которым вообще нет движений на складе. Поменяйте условие на ЕСТЬ НЕ NULL — получите только те, по которым движения были.

Почему нельзя писать = NULL. В логике запроса NULL несравним ни с чем, даже сам с собой: результат сравнения NULL = NULL — не «истина» и не «ложь», а «неизвестно». Условие ГДЕ пропускает только строки, где выражение истинно, поэтому:

  • ГДЕ Поле = NULL — не вернёт ни одной строки;
  • ГДЕ Поле <> NULL — тоже не вернёт ни одной строки;
  • ГДЕ Поле ЕСТЬ NULL — вернёт строки с пустым значением.

Та же ловушка — с обычным отбором. Условие ГДЕ Статус <> &Закрыт не захватит строки, где Статус пришёл как NULL (из соединения). Чтобы учесть и их, пишут ГДЕ ЕСТЬNULL(Статус, "") <> &Закрыт либо ГДЕ Статус <> &Закрыт ИЛИ Статус ЕСТЬ NULL.

Функция ЕСТЬNULL: замена пустого значения на своё

Когда NULL надо не отобрать, а заменить (чаще всего пустой остаток — на 0, пустую строку — на прочерк), применяют функцию ЕСТЬNULL:

ВЫБРАТЬ
    Номенклатура.Наименование КАК Наименование,
    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Количество,
    ЕСТЬNULL(Остатки.Склад, ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)) КАК Склад
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
    ПО Остатки.Номенклатура = Номенклатура.Ссылка

Синтаксис: ЕСТЬNULL(Выражение, ЗначениеЕслиNULL). Если первый аргумент не NULL — вернётся он, иначе — второй. Это полный аналог функции ISNULL из SQL и функции ЕСТЬNULL в СКД.

Несколько практических правил по типам:

  • Тип второго аргумента должен совпадать с первым. Для числового ресурса подставляйте 0, для суммы — 0, для ссылки — пустую ссылку того же справочника, для строки — "". Иначе поле получит составной тип, и дальше с ним неудобно работать.
  • Оборачивайте ресурс до арифметики. ЕСТЬNULL(Остатки.СуммаОстаток, 0) - ЕСТЬNULL(Резерв.СуммаОстаток, 0) считается корректно. А вот Остатки.СуммаОстаток - Резерв.СуммаОстаток при пустом резерве даст NULL: любое арифметическое действие с NULL возвращает NULL.
  • Агрегаты и NULL. СУММА и КОЛИЧЕСТВО пропускают строки с NULL, но если в группе все значения NULL, СУММА вернёт NULL, а не 0. Поэтому обёртка нужна даже после СГРУППИРОВАТЬ ПО: ЕСТЬNULL(СУММА(Остатки.СуммаОстаток), 0).

Сравнение двух инструментов:

ЗадачаОператор ЕСТЬ NULLФункция ЕСТЬNULL
Отобрать пустые строки в ГДЕданет
Заменить пустое на 0 / прочеркнетда
Подготовить поле к арифметикенетда
Проверить условие в ВЫБОРдада

NULL в конструкции ВЫБОР КОГДА

Внутри ВЫБОР (аналог CASE) проверка на NULL подчиняется тем же правилам: ветка КОГДА Поле = NULL никогда не сработает, нужно КОГДА Поле ЕСТЬ NULL.

ВЫБРАТЬ
    Номенклатура.Наименование КАК Наименование,
    ВЫБОР
        КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL
            ТОГДА "Нет движений"
        КОГДА Остатки.КоличествоОстаток = 0
            ТОГДА "Нулевой остаток"
        ИНАЧЕ "В наличии"
    КОНЕЦ КАК Состояние
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
    ПО Остатки.Номенклатура = Номенклатура.Ссылка

Порядок веток важен: сначала отсекаем NULL, потом уже сравниваем с нулём. Если поставить проверку = 0 первой, строки с NULL до неё не дойдут, но и в неё не попадут — уйдут в ИНАЧЕ, и вы получите неверную отметку «В наличии» там, где товара нет.

Если внутри ВЫБОР нужно сравнить два поля, одно из которых может быть NULL, оберните оба: КОГДА ЕСТЬNULL(А, 0) >= ЕСТЬNULL(Б, 0). Без обёртки сравнение с NULL даст «неизвестно», и строка молча провалится в ИНАЧЕ.

NULL во временных таблицах и соединениях

Самый частый источник NULL — пакетный запрос, где данные собирают во временную таблицу, а потом присоединяют к основной выборке левым соединением. Там, где пары нет, появится NULL.

ВЫБРАТЬ
    Заказы.Ссылка КАК Заказ,
    Заказы.Сумма КАК СуммаЗаказа
ПОМЕСТИТЬ ВТ_Заказы
ИЗ
    Документ.ЗаказПокупателя КАК Заказы
;
ВЫБРАТЬ
    ВТ_Заказы.Заказ КАК Заказ,
    ЕСТЬNULL(СУММА(Оплаты.Сумма), 0) КАК Оплачено
ИЗ
    ВТ_Заказы КАК ВТ_Заказы
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РасчётыСКлиентами КАК Оплаты
    ПО Оплаты.Заказ = ВТ_Заказы.Заказ
СГРУППИРОВАТЬ ПО
    ВТ_Заказы.Заказ

Здесь важны два момента. Во-первых, СУММА по заказу без оплат даёт NULL, поэтому стоит обёртка ЕСТЬNULL(..., 0). Во-вторых, если дальше по этому полю строится отбор (ГДЕ Оплачено < СуммаЗаказа), без обёртки неоплаченные заказы выпадут из отчёта — именно те, что вам и нужны.

Отдельная засада — соединение по полю, которое само может быть NULL. Условие ПО Таблица1.Поле = Таблица2.Поле не соединит строки, где хотя бы одно Поле равно NULL: равенства NULL не бывает. Если по смыслу пустые значения должны сопоставляться, приводите их обеими сторонами: ПО ЕСТЬNULL(Таблица1.Поле, &Пусто) = ЕСТЬNULL(Таблица2.Поле, &Пусто).

Про индексы временных таблиц: если присоединяемая временная таблица большая, задайте ИНДЕКСИРОВАТЬ ПО тому полю, по которому идёт соединение. На скорость обработки NULL это не влияет, но убирает полный перебор.

NULL в СКД и табличном документе

В системе компоновки данных NULL проявляется так же, как в запросе набора данных, плюс добавляется поведение при выводе.

В группировках. Если поле, по которому идёт группировка, содержит NULL, в отчёте появится группа с пустым заголовком. Чтобы она читалась, заменяйте значение ещё в запросе набора данных: ЕСТЬNULL(Поле, ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)) или строкой ЕСТЬNULL(Поле, "Не указан").

В ресурсах. Числовые ресурсы в СКД обычно складываются корректно: пустые строки не мешают итогам. Но если выражение ресурса — это разность или отношение полей, оборачивайте слагаемые функцией ЕСТЬNULL, иначе итог по строке с пустым полем станет пустым.

В вычисляемых полях. В СКД доступна та же функция ЕСТЬNULL, что и в языке запросов. Удобнее задать замену один раз в вычисляемом поле компоновки, чем тянуть обёртку через весь запрос.

При выводе в табличный документ. Когда результат запроса выгружается в табличный документ (через ПостроительОтчета, вывод макета или программно), NULL в ячейке отображается как пустое место. Для платёжных и количественных колонок это обычно нежелательно — там ждут 0. Решение всё то же: заменить NULL на 0 в самом запросе, а не форматировать ячейку постфактум.

Не пытайтесь «починить» NULL оформлением ячейки или условным оформлением СКД — оформление меняет вид, но не значение. Отбор, итоги и выгрузка всё равно будут работать с NULL. Правьте данные в запросе.

Частые ошибки и влияние на производительность

Разберём то, что в документации не всегда заметно, а на практике отнимает часы.

Обёртка ЕСТЬNULL вокруг индексированного поля в условии. Запись вида ГДЕ ЕСТЬNULL(Поле, 0) > 0 заставляет СУБД вычислить функцию для каждой строки и не даёт использовать индекс по этому полю. На больших таблицах это заметно. Если по полю есть индекс и NULL там невозможен (поле измерения регистра, реквизит с обязательным заполнением), обёртка лишняя — убирайте её. Там, где NULL реально возможен, по возможности переносите замену в ВЫБРАТЬ, а в ГДЕ используйте ЕСТЬ NULL / ЕСТЬ НЕ NULL — эти операторы индекс использовать могут.

Потеря строк в отборах с <> и НЕ В. Условия Поле <> Значение и Поле НЕ В (&Список) отсекают строки с NULL, хотя по смыслу они часто должны попасть в выборку. Если поле может быть пустым, добавляйте ИЛИ Поле ЕСТЬ NULL.

Арифметика без обёртки. Поле1 * Поле2, Поле1 - Поле2, конкатенация строк — всё это возвращает NULL, если хотя бы один операнд NULL. Самая частая жалоба «в отчёте пустые суммы вместо нулей» — отсюда.

Путаница NULL и Неопределено при чтении выборки. В коде после Запрос.Выполнить().Выбрать() проверяйте поле именно на тот тип, который там есть. Универсально работает ЗначениеЗаполнено(Выборка.Поле) — она считает незаполненными и NULL, и Неопределено, и пустую ссылку, и 0. А прямое сравнение Выборка.Поле = Неопределено для поля, пришедшего из базы как NULL, даст ложь.

Проверьте себя:

Проверьте себя
Какое условие в ГДЕ вернёт строки с пустым значением поля?
Что вернёт СУММА по группе, где все значения ресурса NULL?
Чем заменить NULL прямо в тексте запроса?

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

Чем NULL отличается от нуля и пустой строки?
Ноль и пустая строка — это заполненные значения. NULL означает, что значения нет вообще. Поэтому Поле = 0 и Поле ЕСТЬ NULL проверяют разные вещи, а сравнение NULL = 0 не истинно.
Почему `Поле = NULL` не работает?
В логике запроса NULL несравним ни с чем, результат любого сравнения с ним — «неизвестно». Условие ГДЕ пропускает только истинные строки, поэтому = NULL не вернёт ничего. Нужен оператор ЕСТЬ NULL.
Как заменить NULL на 0 сразу в запросе?
Оберните поле функцией ЕСТЬNULL(Поле, 0) в секции ВЫБРАТЬ. Для строки подставляйте "", для ссылки — пустую ссылку соответствующего справочника.
NULL замедляет запрос?
Сам по себе — нет. Замедляет обёртка ЕСТЬNULL вокруг индексированного поля в условии ГДЕ: она отключает индекс. В отборах используйте ЕСТЬ NULL / ЕСТЬ НЕ NULL, а замену делайте в ВЫБРАТЬ.
Почему после левого соединения пустые суммы вместо нулей?
По несопоставленным строкам ресурс приходит как NULL, а арифметика и агрегаты с NULL дают NULL. Оберните каждое слагаемое и итог функцией ЕСТЬNULL(..., 0).
Как в коде проверить поле выборки на пустоту?
Надёжнее всего ЗначениеЗаполнено(Выборка.Поле) — она считает незаполненными NULL, Неопределено, пустую ссылку и ноль. Прямое = Неопределено для поля, пришедшего из базы как NULL, не сработает.