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