При работе с бухгалтерскими регистрами в 1С программисты часто сталкиваются с ситуацией, когда запрос к виртуальной таблице ОборотыДтКт возвращает одну строку со значением NULL, хотя ожидается полностью пустой результат (0 строк). Давайте разберем, почему так происходит, как работает механизм СУБД под капотом платформы и как правильно писать запросы, чтобы избежать ошибок в логике программы.
Для начала проанализируем ситуацию, описанную в вопросе. В запросе выбирается только поле СуммаОборот. Когда мы обращаемся к виртуальной таблице и не выбираем ни одного измерения (Счет, Субконто, Организация, Период), платформа 1С трансформирует этот запрос в SQL-запрос с использованием агрегатных функций, таких как SUM.
Рассмотрим, как это работает на уровне базы данных. В языке SQL выполнение функции SUM() по набору данных, который не содержит строк (например, из-за жестких фильтров в параметрах виртуальной таблицы), всегда возвращает одну строку со значением NULL. Это стандарт поведения реляционных баз данных: если вы просите вычислить сумму «ничего», результатом будет неопределенность.
Однако, как только мы добавляем в список полей хотя бы одно измерение или реквизит (например, СчетДт), SQL-движок вынужден использовать оператор GROUP BY. В этом случае логика меняется: если данных для группировки нет, то и групп не создается, а значит, результат запроса будет содержать 0 строк. Именно это поведение автор темы и ожидал увидеть изначально.
Давайте посмотрим на исходный код запроса, который вызывает вопросы:
ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.СуммаОборот КАК СуммаОборот
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(
&Дата1,
&Дата2,
,
СчетДт = &СчетДт,
,
СчетКт = &СчетКт,
,
СубконтоДт1 = &НомГруппа
И СубконтоДт2 = &СтатьяЗатрат) КАК ХозрасчетныйОборотыДтКт
В данном случае, если за указанный период между счетами и по указанным субконто не было движений, виртуальная таблица все равно сформирует строку. Если мы попытаемся получить результат этого запроса в коде 1С, цикл Пока Выборка.Следующий() отработает один раз, и переменная Выборка.СуммаОборот будет содержать NULL. Если в дальнейшем эта переменная будет участвовать в математических операциях, программа выдаст ошибку: "Операция не определена для данного типа значений".
Самый простой и надежный способ избежать проблем с дальнейшими вычислениями — это использование встроенной функции языка запросов ЕСТЬNULL. Мы должны явно указать платформе, что вместо «пустоты» (NULL) хотим получить числовой ноль.
Проанализируем исправленный вариант выборки полей:
ВЫБРАТЬ
ЕСТЬNULL(ХозрасчетныйОборотыДтКт.СуммаОборот, 0) КАК СуммаОборот
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(...) КАК ХозрасчетныйОборотыДтКт
Теперь, даже если данных нет, запрос вернет одну строку со значением 0. Это позволит безопасно складывать, вычитать или сравнивать полученный результат в коде модуля.
Если для логики вашей программы критически важно, чтобы при отсутствии данных запрос не возвращал вообще ни одной строки (например, чтобы не заходить внутрь цикла обработки), воспользуемся советом из обсуждения: добавим в поля запроса любое измерение. Даже если оно нам не нужно для вывода, оно заставит СУБД применить группировку.
Рассмотрим пример:
ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.СчетДт КАК СчетДт,
ХозрасчетныйОборотыДтКт.СуммаОборот КАК СуммаОборот
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(...) КАК ХозрасчетныйОборотыДтКт
В такой конфигурации, если оборотов нет, Выборка.Следующий() сразу вернет Ложь, и тело цикла будет пропущено.
Обратим внимание на сообщение №3 из темы. В нем дается важный совет по оптимизации: при фильтрации по субконто в виртуальных таблицах крайне рекомендуется передавать массив видов субконто в соответствующий параметр таблицы. Это третий параметр в ОборотыДтКт.
Выясним причину: если мы не передаем массив видов субконто, платформа 1С вынуждена анализировать все возможные виды субконто, которые могут быть назначены счету. Это приводит к избыточным соединениям (JOIN) в SQL-запросе и может замедлить выполнение. Более того, при сложных условиях фильтрации отсутствие явно заданных типов может приводить к некорректному сопоставлению полей СубконтоДт1, СубконтоДт2 и т.д.
Разберем, как правильно подготовить параметры в коде 1С:
// Подготавливаем массив типов субконто
МассивСубконто = Новый Массив;
МассивСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.НоменклатурныеГруппы);
МассивСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.СтатьиЗатрат);
Запрос.УстановитьПараметр("ВидыСубконто", МассивСубконто);
И далее в самом тексте запроса используем этот массив:
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(
&Дата1,
&Дата2,
&ВидыСубконто, // Передаем массив здесь!
СчетДт = &СчетДт,
,
СчетКт = &СчетКт,
,
СубконтоДт1 = &НомГруппа
И СубконтоДт2 = &СтатьяЗатрат) КАК ХозрасчетныйОборотыДтКт
Подводя итог нашему разбору, выделим основные правила при работе с ресурсами виртуальных таблиц в 1С:
ЕСТЬNULL при получении ресурсов (сумм, количеств), если вы выбираете их без сопутствующих измерений. Это застрахует вас от неожиданных NULL в расчетах.Счет или Период. Если вам нужно значение 0 в любом случае — используйте агрегацию ресурсов без измерений.СубконтоДт1 = &Значение внутри параметров виртуальной таблицы работают быстрее, чем аналогичные условия в предложении ГДЕ, так как они накладываются на этапе формирования временных таблиц в СУБД.Понимание этих нюансов позволяет писать более стабильный и производительный код, избегая трудноуловимых ошибок, связанных с обработкой «пустых» данных в бухгалтерском учете.