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