Как убрать NULL из результата запроса и подставить ноль в 1С 8.3

· 10 мин

Содержание

Заменить 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.

Несколько приёмов, которые стоит держать в голове:

  1. Оборачивайте результат вычитания. В ЕСТЬNULL(А, 0) - ЕСТЬNULL(Б, 0) каждый операнд защищают по отдельности. Если обернуть только разность, NULL внутри уже «съест» всё выражение.
  2. Замена не только нулём. Вторым аргументом может быть любое значение совместимого типа: ЕСТЬNULL(Склад, &СкладПоУмолчанию), ЕСТЬNULL(Комментарий, "").
  3. Вложенность. ЕСТЬNULL(ЕСТЬNULL(ЦенаАкция, ЦенаБаза), 0) — сначала пробуем акционную цену, потом базовую, потом ноль.
  4. Тип второго аргумента. Он задаёт тип колонки. Для числового ресурса подставляйте число 0, а не строку "0", иначе колонка станет составного типа.

ЕСТЬNULL(Поле, 0) и условие ГДЕ Поле ЕСТЬ НЕ NULL решают разные задачи. Первое заменяет значение и строку сохраняет. Второе выбрасывает строку из результата целиком. Если по позициям без остатка нужна строка с нулём — только ЕСТЬNULL.

Обработка NULL в программном коде модулей

Лучшее правило: не пускать NULL в код. Если значение приходит из запроса, оберните его в ЕСТЬNULL ещё в тексте запроса — тогда в коде вы работаете с нормальным числом.

Когда NULL всё-таки добрался до выборки или до реквизита составного типа, проверяют тип значения:

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Остаток = Выборка.Остаток;
    Если ТипЗнч(Остаток) = Тип("Null") Тогда
        Остаток = 0;
    КонецЕсли;
    // дальше Остаток — гарантированно число
КонецЦикла;

ТипЗнч(Значение) = Тип("Null") — точная проверка: она реагирует именно на NULL и не путает его с нулём или пустой строкой.

Есть и короткий путь — ЗначениеЗаполнено(Остаток). Для NULL функция возвращает Ложь, значит, можно писать Если НЕ ЗначениеЗаполнено(Остаток) Тогда Остаток = 0. Но учтите: для настоящего нуля ЗначениеЗаполнено тоже вернёт Ложь. Если по логике задачи важно отличать «нет данных» от «значение равно нулю», берите ТипЗнч, а не ЗначениеЗаполнено.

Частая ошибка — сравнение через равенство:

// так NULL не поймать — условие всегда ложно
Если Остаток = NULL Тогда ...

В отличие от языка запросов, где для этого есть ЕСТЬ NULL, во встроенном языке сравнивать значение с литералом NULL через = бессмысленно. Проверяйте тип.

Выбор способа под ситуацию:

Где заменять NULL на 0 в вашем случае?

Замена 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 в конкретный запрос отчёта, либо пересчитывает и дозаписывает реквизиты по данным регистров. И то и другое пишется под конкретную задачу.

Короткий чек-лист, чтобы ничего не забыть:

Проверка перед сдачей отчёта или обработки
Выполнено: 0 / 7

Типичные ошибки и как их распознать

Диагностика: выберите симптом

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

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