При построении сложных отчетов в 1С часто используется схема с предварительной подготовкой временных таблиц и их последующим левым соединением. Однако если одна из временных таблиц оказывается пустой, поведение запроса и дальнейшего кода обработки результатов может привести к искажению данных и критическим ошибкам выполнения. Проанализируем причину возникновения этой проблемы и разберем способы ее решения.
Рассмотрим ситуацию, когда в отчете выполняется группировка расходов из временной таблицы ТаблицаРасходов с использованием ЛЕВОЕ СОЕДИНЕНИЕ по условию ПО (ИСТИНА):
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
ТаблицаРасходов.Период КАК Период,
СУММА(ТаблицаРасходов.СуммаФакт) КАК СуммаРасходов
ИЗ
ТаблицаРасходов КАК ТаблицаРасходов
СГРУППИРОВАТЬ ПО
ТаблицаРасходов.Период) КАК ИтоговаяТаблицаРасходов
ПО (ИСТИНА)
Выясним причину, почему данная конструкция ломает расчеты, если временная таблица ТаблицаРасходов не содержит ни одной строки:
СГРУППИРОВАТЬ ПО и агрегатными функциями по пустой таблице возвращает 0 строк (а не одну строку с нулевыми суммами).NULL. В результате периоды и суммы расходов становятся не определены.ПО ИтоговаяТаблицаДоходов.Период = ИтоговаяТаблицаДоходов.Период, таблица связывается сама с собой, из-за чего сопоставление периодов с расходами полностью теряет смысл.Попытка исправить значение периода с помощью функции ЕСТЬNULL(Период, 0) создаст новые проблемы на стороне встроенного языка 1С. Посмотрим, к каким последствиям это приводит:
0 меняет тип поля с даты на составной тип (Дата, Число). При попытке выполнить сравнение в коде:
Если СтрокаПериод.Период >= СметаЗатрат.ДатаНачала Тогда
платформа генерирует исключение: «Операции допустимы только для примитивных типов (булево, число, дата...)», так как система не может сравнить числовое значение 0 с типом Дата.ПрефиксПериода = Формат(СтрокаПериод.Период, "ДФ=MMyy");
Если в поле Период находится значение 0 или NULL, функция Формат() возвращает пустую строку. На каждой итерации цикла генерируются одинаковые псевдонимы колонок (например, СуммаФакт вместо СуммаФакт0124), из-за чего запрос завершается с ошибкой повторения имен полей.Разберем проверенные варианты построения алгоритма, которые позволяют избежать ошибок при отсутствии записей во временных таблицах.
Перед выполнением основного запроса проверим, есть ли записи во временной таблице ТаблицаРасходов. Это можно сделать быстрым точечным запросом:
ЗапросПроверки = Новый Запрос;
ЗапросПроверки.МенеджерВременныхТаблиц = МенеджерВТ;
ЗапросПроверки.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1 1 ИЗ ТаблицаРасходов КАК ТаблицаРасходов";
РезультатПроверки = ЗапросПроверки.Выполнить();
Если РезультатПроверки.Пустой() Тогда
// Заполняем таблицу одной технической записью с нулевой суммой и корректной датой
// либо меняем логику формирования текста основного запроса
КонецЕсли;
Наиболее надежный архитектурный подход — исключить цепочки левых соединений по ПО (ИСТИНА) и сверить все показатели через ОБЪЕДИНИТЬ ВСЕ в одну промежуточную таблицу.
Проанализируем пример такого запроса:
ВЫБРАТЬ
Доходы.Период КАК Период,
Доходы.СуммаДоходов КАК СуммаДоходов,
0 КАК СуммаРасходов
ПОМЕСТИТЬ ВТ_СводныеДанные
ИЗ
ТаблицаДоходов КАК Доходы
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Расходы.Период,
0,
Расходы.СуммаФакт
ИЗ
ТаблицаРасходов КАК Расходы
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Свод.Период КАК Период,
СУММА(Свод.СуммаДоходов) КАК СуммаДоходов,
СУММА(Свод.СуммаРасходов) КАК СуммаРасходов
ПОМЕСТИТЬ ВТ_ИтоговыеПоказатели
ИЗ
ВТ_СводныеДанные КАК Свод
СГРУППИРОВАТЬ ПО
Свод.Период
Преимущества данного метода:
NULL в полях дат и сумм.Период всегда остаётся строгим типом Дата, что предотвращает ошибки в функциях Формат() и при сравнении дат в коде BSL.