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