Работа с виртуальными таблицами в 1С — это база, на которой строится большинство отчетов. Однако новички (а иногда и опытные разработчики) сталкиваются с ситуацией, когда запрос к таблице ОстаткиИОбороты возвращает пустой результат или некорректные данные, особенно при попытке построить отчет в разрезе периодов (месяцев). Разберем по шагам, где кроются ошибки в представленном запросе и как их исправить.
Проанализируем работу виртуальной таблицы ОстаткиИОбороты. По умолчанию она работает по принципу: нет оборотов — нет строки. Если вы выбрали периодичность «Месяц» и в феврале по конкретному договору не было ни одного прихода или расхода, то запрос просто не вернет строку за февраль, даже если на 1 февраля был огромный остаток.
Рассмотрим, как это решается. В системе компоновки данных (СКД) это настраивается галочкой «Роль -> Период -> Дополнение периодов». Но если мы пишем «чистый» запрос, нам нужно:
ЛЕВЫМ СОЕДИНЕНИЕМ.Посмотрим на этот фрагмент кода автора:
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ВерсииРасчетов.СрезПоследних(
&НачалоПланируемогоГода,
гптэТипГрафикаФИ = ЗНАЧЕНИЕ(Перечисление.гптэТипыГрафикаФИ.ДляБюджета)
) КАК ВерсииРасчетовСрезПоследних
ПО РасчетыСКонтрагентамиГрафикиОстатки.ВерсияГрафика = ВерсииРасчетовСрезПоследних.Регистратор
Здесь кроется критическая ошибка. Если на дату &НачалоПланируемогоГода в регистре сведений нет записи о версии графика для конкретного договора, внутреннее соединение полностью удалит строку с остатком из результата. Выясним причину: ВНУТРЕННЕЕ СОЕДИНЕНИЕ требует обязательного наличия данных в обеих таблицах. Если версия графика была установлена чуть позже или по договору еще нет настроек, остаток «пропадет».
Решение: Всегда используйте ЛЕВОЕ СОЕДИНЕНИЕ при работе со справочными данными или настройками, отсутствие которых не должно приводить к исчезновению финансовых показателей.
Проанализируем финальную часть запроса автора:
ЛЕВОЕ СОЕДИНЕНИЕ Движения КАК Движения
ПО (Движения.Период < Месяца.Период)
Автор пытается вычислить остаток на начало месяца путем сложения всех предыдущих движений. Это классический подход SQL для расчета нарастающего итога, но он требует обязательной группировки. Без конструкции СГРУППИРОВАТЬ ПО и агрегатной функции СУММА(Движения.СуммаОборот) такой запрос порождает декартово произведение. Каждая строка месяца соединяется с каждой строкой движения, что приводит к геометрическому росту количества строк и неверным цифрам.
Рассмотрим более эффективный и надежный способ получения остатков на начало каждого месяца. Вместо того чтобы вручную объединять Обороты и Остатки, воспользуемся возможностями виртуальной таблицы ОстаткиИОбороты с установленной периодичностью.
Шаг 1: Подготовка периодов. Сначала создадим временную таблицу, содержащую все начала месяцев в выбранном интервале.
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&НачалоПериода, МЕСЯЦ, Номера.Номер - 1) КАК Период
ПОМЕСТИТЬ ВсеМесяцы
ИЗ
(ВЫБРАТЬ 1 КАК Номер ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2 ... ВЫБРАТЬ 12) КАК Номера
ГДЕ
Номера.Номер <= РАЗНОСТЬДАТ(&НачалоПериода, &КонецПериода, МЕСЯЦ) + 1
;
Шаг 2: Получение данных из виртуальной таблицы. Важно передавать условия фильтрации (по ЦФО, договору) непосредственно в параметры виртуальной таблицы, а не в секцию ГДЕ. Это значительно ускоряет работу и гарантирует корректность расчета итогов.
ВЫБРАТЬ
Регистр.Период КАК Период,
Регистр.ПредметГрафика КАК Договор,
Регистр.СуммаНачальныйОстаток КАК ОстатокНаНачало,
Регистр.СуммаОборот КАК ОборотЗаМесяц
ПОМЕСТИТЬ ДанныеРегистра
ИЗ
РегистрНакопления.РасчетыСКонтрагентамиГрафики.ОстаткиИОбороты(
&НачалоПериода,
&КонецПериода,
Месяц, ,
ЭлементСтруктурыЗадолженности = &ОсновнойДолг
) КАК Регистр
;
Шаг 3: Соединение с дополнением. Теперь соединим таблицу всех месяцев с данными регистра. Используем ЛЕВОЕ СОЕДИНЕНИЕ от таблицы месяцев, чтобы не потерять периоды без активности.
ВЫБРАТЬ
ВсеМесяцы.Период КАК Период,
Данные.Договор КАК Договор,
ЕСТЬNULL(Данные.ОстатокНаНачало, 0) КАК Сумма
ИЗ
ВсеМесяцы КАК ВсеМесяцы
ЛЕВОЕ СОЕДИНЕНИЕ ДанныеРегистра КАК Данные
ПО ВсеМесяцы.Период = Данные.Период
1 = 1: В коде автора часто встречается это условие. Это не ошибка, а распространенный прием для динамического формирования текста запроса в коде. Программисту проще добавлять новые условия через " И <Условие>", если в базовом тексте уже есть хотя бы одно условие. На производительность это не влияет, так как оптимизатор запросов SQL удаляет такие константы.ВЫРАЗИТЬ(ПредметГрафика КАК Справочник.ДоговорыКонтрагентов) в параметрах виртуальной таблицы, убедитесь, что тип данных в регистре действительно является составным. Если поле ПредметГрафика имеет только один тип, лишние преобразования замедлят запрос.&КонецПериода, помните, что дата без указания времени (например, '2023-12-31') — это начало дня. Чтобы включить движения последнего дня, используйте объект Граница с видом ВключаяГраницу.Подводя итог, проанализируем: чтобы ваш запрос всегда возвращал остатки, избавьтесь от внутренних соединений с необязательными данными, вынесите фильтры в параметры виртуальных таблиц и обеспечьте дополнение периодов через соединение с календарем или справочником периодов. Это позволит получить полную картину движений и остатков даже в те месяцы, когда активности по счету не было.