Как в запросе 1С отловить и заменить NULL
Содержание
Проверить поле на 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 отличается от нуля и пустой строки?
Поле = 0 и Поле ЕСТЬ NULL проверяют разные вещи, а сравнение NULL = 0 не истинно.Почему `Поле = NULL` не работает?
ГДЕ пропускает только истинные строки, поэтому = NULL не вернёт ничего. Нужен оператор ЕСТЬ NULL.Как заменить NULL на 0 сразу в запросе?
ЕСТЬNULL(Поле, 0) в секции ВЫБРАТЬ. Для строки подставляйте "", для ссылки — пустую ссылку соответствующего справочника.NULL замедляет запрос?
ЕСТЬNULL вокруг индексированного поля в условии ГДЕ: она отключает индекс. В отборах используйте ЕСТЬ NULL / ЕСТЬ НЕ NULL, а замену делайте в ВЫБРАТЬ.Почему после левого соединения пустые суммы вместо нулей?
ЕСТЬNULL(..., 0).Как в коде проверить поле выборки на пустоту?
ЗначениеЗаполнено(Выборка.Поле) — она считает незаполненными NULL, Неопределено, пустую ссылку и ноль. Прямое = Неопределено для поля, пришедшего из базы как NULL, не сработает.