NULL в запросах 1С: откуда берётся пустота и как её убрать через ЕСТЬNULL
Содержание
«Есть NULL» в 1С означает, что поле в результате запроса не содержит вообще никакого значения: ни нуля, ни пустой строки, ни пустой ссылки. Исправляют это функцией ЕСТЬNULL(Поле, ЗначениеЗамены) прямо в тексте запроса, например ЕСТЬNULL(Остатки.КоличествоОстаток, 0).
Типичная ситуация: программист собирает отчёт «номенклатура и остатки», у половины товаров в колонке остатка пусто, итог по группе тоже пустой, а в модуле формы падает Преобразование значения к типу Число не может быть выполнено. Причина одна: в данных пришёл NULL, и его никто не обработал.
Что такое NULL в 1С и чем он отличается от пустых значений
NULL — отдельный тип значения в 1С:Предприятии 8.3. Он означает «значения нет совсем». В базе у реквизита справочника или документа так не бывает: у реквизита всегда есть пустое значение своего типа. NULL рождается в момент выполнения запроса.
Сравните четыре «пустоты», которые часто путают:
| Значение | Что это | Где встречается |
|---|---|---|
NULL | Значения нет, строки в связанной таблице не нашлось | Результат запроса |
Неопределено | Пустое значение составного типа | Реквизиты составного типа, переменные |
| Пустая ссылка | Справочник.Контрагенты.ПустаяСсылка() | Незаполненный ссылочный реквизит |
0, "", пустая дата | Пустое значение примитивного типа | Числа, строки, даты |
В языке запросов есть две похожие записи, их тоже путают:
- 🔹
Поле ЕСТЬ NULL— условие. Возвращает Истина или Ложь. Пишется с пробелом. Отрицание:НЕ Поле ЕСТЬ NULL. - 🔹
ЕСТЬNULL(Поле, Замена)— функция. Возвращает само поле, а если оно NULL — значение замены. Пишется слитно.
В модуле NULL проверяют так:
Если Выборка.Сумма = NULL Тогда
// значения нет
КонецЕсли;
Если ТипЗнч(Выборка.Сумма) = Тип("Null") Тогда
// то же самое, явная проверка типа
КонецЕсли;
Функция ЗначениеЗаполнено(NULL) возвращает Ложь. Она удобна, когда пустой ноль и NULL для вас означают одно и то же. Если их нужно различать — проверяйте тип.
Откуда в запросе берётся NULL: типовые сценарии
NULL почти всегда — следствие структуры запроса, а не порчи данных. Вот где он появляется.
Левое соединение без пары
ЛЕВОЕ СОЕДИНЕНИЕ берёт все строки левой таблицы. Если справа подходящей строки нет, все поля правой таблицы будут NULL.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар,
Остатки.КоличествоОстаток КАК Остаток
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Номенклатура.Ссылка = Остатки.Номенклатура
Товар без движений по регистру попадёт в результат, но Остаток у него будет NULL, а не 0. Виртуальная таблица остатков не хранит нулевые строки, поэтому такой случай встречается постоянно.
Так же ведут себя ПРАВОЕ СОЕДИНЕНИЕ (NULL слева) и ПОЛНОЕ СОЕДИНЕНИЕ (NULL с любой стороны).
Обращение через точку к пустой ссылке
Запись Документ.Контрагент.ИНН внутри запроса платформа превращает в неявное левое соединение. Если Контрагент не заполнен, ИНН придёт как NULL.
Поле составного типа
Регистратор, субконто, «Документ основание» — поля составного типа. Запрос Движения.Регистратор.Склад вернёт NULL для тех регистраторов, у которых реквизита Склад нет. Платформа не выдаёт ошибку, она просто подставляет NULL.
Агрегатные функции
СУММАпо группе, где все значения NULL, вернёт NULL.МАКСИМУМиМИНИМУМпо пустому набору — тоже NULL.КОЛИЧЕСТВО(Поле)строки с NULL не считает, аКОЛИЧЕСТВО(*)считает все строки.
Объединение запросов
В ОБЪЕДИНИТЬ ВСЕ во второй части иногда пишут NULL КАК Сумма как заглушку для колонки. Потом этот NULL всплывает в итогах.
Чем NULL опасен: поведение в выражениях и условиях
NULL «заражает» всё, с чем соприкасается. Это главное, что нужно помнить.
- ⚠️ Арифметика:
Цена * NULL= NULL. Сумма в строке пропадёт целиком. - ⚠️ Сравнение:
NULL = 0,NULL <> 0,NULL > 5не дают Истину. Строка с NULL не пройдёт ни одно из этих условий. - ⚠️ Условие отбора:
ГДЕ Остатки.КоличествоОстаток <> 1молча выкинет товары без остатка — для них поле NULL, а не 0. - ⚠️ Модуль:
Выборка.Остаток + 1при NULL даёт исключениеПреобразование значения к типу Число не может быть выполнено. - ⚠️ Отчёт на СКД: ячейка пустая, итог по группе может отличаться от ожидаемого.
Вот как это выглядит на числах. Товар А: цена 100, остаток 5. Товар Б: цена 200, остатка нет.
| Выражение | Товар А | Товар Б |
|---|---|---|
Цена * Остатки.КоличествоОстаток | 500 | NULL |
Цена * ЕСТЬNULL(Остатки.КоличествоОстаток, 0) | 500 | 0 |
Условие Остаток = 0 | Ложь | не проходит |
Условие ЕСТЬNULL(Остаток, 0) = 0 | Ложь | Истина |
Как найти, где именно появляется NULL
Пустая ячейка в отчёте ещё не говорит, откуда она. Ищите по шагам.
- Выделите запрос. Скопируйте текст запроса из модуля или из схемы компоновки данных.
- Выполните его отдельно в консоли запросов (внешняя обработка, которую используют почти все разработчики) или в отладчике.
- Найдите колонку с NULL. В консоли такие значения обычно видны как
NULLили пустое поле с особой отметкой. - Определите источник поля. Посмотрите, из какой таблицы оно берётся и как эта таблица присоединена.
- Проверьте вид соединения. Если таблица справа в
ЛЕВОМ СОЕДИНЕНИИ— это ваш случай. - Проверьте разыменование через точку. Цепочки вида
Ссылка.Реквизит.Реквизитдают NULL при пустом первом звене. - Добавьте контрольную колонку, чтобы отделить «нет строки» от «есть строка с пустым значением»:
ВЫБОР
КОГДА Остатки.Номенклатура ЕСТЬ NULL
ТОГДА "Нет строки в регистре"
ИНАЧЕ "Строка есть"
КОНЕЦ КАК Диагностика
Проверяйте на NULL именно поле соединения (Остатки.Номенклатура), а не числовой ресурс. Так вы увидите, что строки справа нет вовсе.
В отладчике NULL показывается в окне «Вычислить выражение» и в табло со значением Null и типом Null. Поставьте точку останова на строке, где падает исключение, и посмотрите, какое поле выборки пришло пустым.
Как исправить NULL в запросе: пять рабочих способов
Способ 1. Функция ЕСТЬNULL
Самый частый и простой путь. Второй параметр — то, что подставится вместо NULL.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток,
ЕСТЬNULL(Цены.Цена, 0) КАК Цена
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Номенклатура.Ссылка = Остатки.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
ПО Номенклатура.Ссылка = Цены.Номенклатура
Замена должна быть того же типа, что поле: для чисел 0, для строк "", для ссылок ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка).
Способ 2. Конструкция ВЫБОР
Нужна, когда вместо NULL нужно подставить разное в зависимости от условия, или когда важно отличить «нет строки» от «ноль».
ВЫБОР
КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL
ТОГДА "Нет на складе"
КОГДА Остатки.КоличествоОстаток < 0
ТОГДА "Отрицательный остаток"
ИНАЧЕ "В наличии"
КОНЕЦ КАК Статус
Способ 3. Внутреннее соединение вместо левого
Если строки без пары вам не нужны, NULL не нужен тоже. Замените ЛЕВОЕ СОЕДИНЕНИЕ на ВНУТРЕННЕЕ СОЕДИНЕНИЕ — в результате останутся только товары с остатком.
Способ 4. Отбор в параметрах виртуальной таблицы
Условие на правую таблицу в ГДЕ превращает левое соединение в фактически внутреннее и роняет строки. Перенесите отбор в параметры виртуальной таблицы или в условие ПО:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
&Дата,
Склад = &Склад) КАК Остатки
ПО Номенклатура.Ссылка = Остатки.Номенклатура
Так товары без остатка на выбранном складе останутся в выборке с NULL, который затем закрывает ЕСТЬNULL.
Способ 5. Обработка в модуле
Если запрос менять нельзя (типовой, на поддержке), обработайте значение в коде:
Остаток = ?(Выборка.Остаток = NULL, 0, Выборка.Остаток);
Типовую конфигурацию на поддержке правьте через расширение, а не снятием с поддержки — иначе следующее обновление станет заметно сложнее.
| Способ | Когда подходит | Риски |
|---|---|---|
ЕСТЬNULL | Нужен ноль или пустое значение вместо NULL | Теряется разница «нет строки» / «ноль» |
ВЫБОР ... ЕСТЬ NULL | Нужно разное поведение | Громоздкий текст запроса |
| Внутреннее соединение | Строки без пары не нужны | Пропадут товары без остатка |
| Отбор в параметрах | Отбор по правой таблице | Нужно аккуратно перенести условия |
| Проверка в модуле | Запрос менять нельзя | Логика размазана между запросом и кодом |
Ошибки, которые допускают при обработке NULL
- ❌ ЕСТЬNULL после агрегации вместо до.
СУММА(ЕСТЬNULL(Поле, 0))иЕСТЬNULL(СУММА(Поле), 0)дают разный результат, когда в группе нет ни одной строки. Если нужен ноль в итоговой строке — нужен второй вариант. - ❌ Сравнение
= NULLв запросе. В языке запросов так не работает . ПишитеЕСТЬ NULL. - ❌ Замена не того типа.
ЕСТЬNULL(Склад, 0)для ссылочного поля даст колонку составного типа, и отбор по складу начнёт вести себя странно. - ❌ Путаница NULL и Неопределено. Пустой реквизит составного типа — это
Неопределено, а не NULL. ПроверкаЕСТЬ NULLего не поймает. - ❌ ЕСТЬNULL на всякий случай на каждом поле. Запрос становится нечитаемым, а реальные проблемы соединений маскируются нулями.
Как не допускать NULL в новых запросах
Несколько правил, которые экономят время на отладку:
- Каждый раз, когда пишете
ЛЕВОЕ СОЕДИНЕНИЕ, сразу оборачивайте поля правой таблицы, которые идут в расчёт. - Отборы по правой таблице ставьте в параметры виртуальной таблицы или в
ПО, а не вГДЕ. - Для полей составного типа используйте
ВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг)перед обращением к реквизиту. Так понятнее, где ожидать NULL. - В
ОБЪЕДИНИТЬподставляйте заглушки нужного типа:0 КАК Сумма, а неNULL КАК Сумма, если колонка потом суммируется. - В конструкторе запроса проверяйте вкладку «Связи»: галочка «Все» у таблицы и есть левое соединение.
Чем искать NULL автоматически
Ручной просмотр подходит для одного отчёта. Для поиска по базе и коду есть инструменты.
- 🔍 Консоль запросов. Добавьте в запрос
ГДЕ Поле ЕСТЬ NULLи сразу увидите все проблемные строки. - 🔍 Синтакс-помощник и поиск по конфигурации в Конфигураторе. Глобальный поиск по
ЛЕВОЕ СОЕДИНЕНИЕпоказывает места, где NULL вероятен. - 🔍 Отладчик с остановкой по ошибке. Включите в настройках отладки остановку по ошибке: выполнение встанет на строке, где NULL попал в арифметику.
- 🔍 Проверка кода в EDT. Среда 1С:EDT и подключаемые к ней проверки находят часть небезопасных обращений.
- 🔍 Проверочный запрос на «висящие» ссылки. Левое соединение документа со справочником и отбор
Справочник.Ссылка ЕСТЬ NULLпоказывают битые ссылки (<Объект не найден>), из-за которых тоже приходят пустые значения.