Почему запрос с агрегатной функцией СУММА возвращает NULL при пустой таблице и как этого избежать

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Суть проблемы: неожиданный результат агрегации

Представим стандартную ситуацию: мы создаем временную таблицу, накладываем на нее фильтр, который отсекает все записи (делает таблицу пустой), а затем пытаемся посчитать сумму по какому-либо полю. Проанализируем пример кода:


// Шаг 1: Создаем ВТ с данными
ВЫБРАТЬ
    "А" КАК Поле,
    100 КАК Значение
ПОМЕСТИТЬ ВременнаяТаблица
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    "Б",
    200
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Создаем пустую ВТ (условие ГДЕ заведомо ложное)
ВЫБРАТЬ
    ВременнаяТаблица.Поле КАК Поле,
    ВременнаяТаблица.Значение КАК Значение
ПОМЕСТИТЬ ВременнаяТаблицаСОтбором
ИЗ
    ВременнаяТаблица КАК ВременнаяТаблица
ГДЕ
    ВременнаяТаблица.Поле = "НесуществующееЗначение"
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Агрегация
ВЫБРАТЬ
    СУММА(ВременнаяТаблицаСОтбором.Значение) КАК Значение
ИЗ
    ВременнаяТаблицаСОтбором КАК ВременнаяТаблицаСОтбором

Казалось бы, если в таблице ВременнаяТаблицаСОтбором нет ни одной записи, то и итоговый запрос должен вернуть пустой результат. Однако на практике запрос возвращает одну строку, в которой значение поля Значение равно NULL. Для разработчика, ожидающего, что метод Запрос.Выполнить().Пустой() вернет значение Истина, это может стать причиной ошибки в логике программы. При необходимости детального анализа таких ситуаций можно использовать инструмент для отладки запросов с выгрузкой временных таблиц.

Различие между глобальной агрегацией и группировкой

Для понимания причин такого поведения важно различать два режима работы агрегатных функций в языке запросов SQL, на котором базируется платформа 1С:

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

Рассмотрим пример с группировкой, который вернет пустой результат:


ВЫБРАТЬ
    ВременнаяТаблицаСОтбором.Поле КАК Поле,
    СУММА(ВременнаяТаблицаСОтбором.Значение) КАК Значение
ИЗ
    ВременнаяТаблицаСОтбором КАК ВременнаяТаблицаСОтбором
СГРУППИРОВАТЬ ПО
    ВременнаяТаблицаСОтбором.Поле

В данном случае, если исходная таблица пуста, запрос не вернет ни одной строки, что часто является более ожидаемым поведением для бизнес-логики.

Особенности функции КОЛИЧЕСТВО в сравнении с СУММА

Интересно проанализировать ситуацию с другими агрегатными функциями. Выясним причину, по которой КОЛИЧЕСТВО ведет себя иначе. Согласно математической логике и стандартам SQL:

Тем не менее, даже если запрос с КОЛИЧЕСТВО(*) вернет 0, это все равно будет одна строка с нулем, а не отсутствие строк.

Практические способы обработки пустого результата

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

Способ 1: Использование функции ЕСТЬNULL

Это самый распространенный и рекомендуемый метод. Мы принудительно заменяем неопределенное значение на числовой ноль. Это не уберет "лишнюю" строку из результата, но предотвратит ошибки при дальнейших арифметических операциях в коде 1С.


ВЫБРАТЬ
    ЕСТЬNULL(СУММА(ВТ.Значение), 0) КАК Значение
ИЗ
    ВТ

Способ 2: Использование предложения ИМЕЮЩИЕ

Если нам критично, чтобы запрос вообще не возвращал строк, если данных нет, мы можем использовать условие ИМЕЮЩИЕ. Проанализируем пример:


ВЫБРАТЬ
    СУММА(ВТ.Значение) КАК Значение
ИЗ
    ВТ
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(*) > 0

В этом случае условие КОЛИЧЕСТВО(*) > 0 отсечет ту самую единственную строку с NULL, так как фактически записей в выборке нет.

Влияние на итоги и соединения

Важно помнить, что поведение механизмов расчета итогов (предложение ИТОГИ) отличается от простых агрегатных функций. Если в запросе присутствуют ИТОГИ, но нет детальных записей, удовлетворяющих условиям ГДЕ, результат запроса будет абсолютно пустым, включая строку общих итогов.

Также стоит учитывать поведение при соединениях таблиц. Например, при использовании ЛЕВОЕ СОЕДИНЕНИЕ с пустой таблицей, количество строк в результате будет соответствовать левой таблице, а все поля правой таблицы будут заполнены NULL. Однако если мы используем агрегацию по полям правой таблицы, мы снова можем столкнуться с описанным выше эффектом глобальной группировки. Для лучшего понимания работы подобных механизмов можно воспользоваться материалом с объяснением работы соединений в запросах для начинающих.

Резюме для разработчика

Подведем итоги нашего анализа:

Понимание этих нюансов работы движка запросов позволяет писать более надежный код и избегать трудноуловимых ошибок при расчете итоговых показателей в отчетах и документах.

← На главную