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