Как в СКД рассчитать обороты взаиморасчетов с индивидуальным интервалом дат для каждого сотрудника?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

Разберем нестандартную задачу компоновки данных: требуется сформировать отчет по регистру накопления Взаиморасчеты, где для каждого сотрудника (менеджера) задается собственный интервал анализа в днях. Дата окончания для всех строк равна текущей дате, а дата начала рассчитывается динамически для каждого сотрудника: ТекущаяДата - ИнтервалДней. При этом в отчете необходимо вывести суммы прихода и расхода за этот персональный период.

Проанализируем архитектурные ограничения платформы 1С:Предприятие, с которыми мы сталкиваемся при реализации подобных условий, и подробно рассмотрим оптимальные варианты решения.

Архитектурные ограничения виртуальных таблиц и разыменования

Первая сложность заключается в специфике работы виртуальных таблиц регистров накопления, таких как Обороты или ОстаткиИОбороты. Параметры начала и окончания периода виртуальной таблицы принимают только скалярные значения или фиксированные параметры запроса. Передать динамическое выражение даты из строки группировки внутрь параметров виртуальной таблицы в рамках единого запроса невозможно.

Вторая потенциальная проблема кроется в обращении к реквизитам через документ-регистратор, например Регистратор.Контрагент.Сотрудник. Поле Регистратор имеет составной тип данных. Разыменование составного типа приводит к генерации неявных соединений (LEFT JOIN) со всеми таблицами документов, являющихся регистраторами для данного регистра. Это способно катастрофически замедлить выполнение запроса на реальной базе данных. По возможности измерение Контрагент следует брать напрямую из регистра, либо ограничивать типы с помощью конструкции ВЫРАЗИТЬ.

Вариант 1. Оптимальный запрос через временные таблицы и таблицу движений

Рассмотрим наиболее производительное решение в рамках одного набора данных «Запрос». Чтобы отчет работал быстро и не выполнял полное сканирование всей таблицы движений регистра за много лет, разделим расчет на четкие этапы с использованием временных таблиц.

Разберем по шагам логику построения такого запроса:

  1. Формирование таблицы сотрудников и их персональных дат. Выбираем сотрудников, получаем значение интервала и рассчитываем дату начала с помощью функции ДОБАВИТЬКДАТЕ. Обязательно приводим даты к началу и концу дня с помощью функций НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА.
  2. Определение общей нижней границы. Вычисляем минимальную дату среди всех рассчитанных дат начала. Это критически важно для производительности СУБД, так как позволяет задействовать индекс по полю Период.
  3. Выборка движений регистра. Обращаемся к физической таблице движений регистра, накладывая жесткий фильтр: движения должны входить в интервал от глобальной минимальной даты до конца текущего дня.
  4. Соединение и агрегация. Соединяем таблицу сотрудников с предварительно отфильтрованными движениями, проверяя попадание каждого движения в персональный интервал сотрудника, и агрегируем суммы прихода и расхода.

Посмотрим на пример реализации данного алгоритма на языке запросов 1С:


ВЫБРАТЬ
    Контрагенты.Ссылка КАК Контрагент,
    Контрагенты.Сотрудник КАК Сотрудник,
    НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ТекущаяДата, ДЕНЬ, -ЕСТЬNULL(Контрагенты.Сотрудник.Интервал, 0)), ДЕНЬ) КАК ДатаНачала,
    КОНЕЦПЕРИОДА(&ТекущаяДата, ДЕНЬ) КАК ДатаОкончания
ПОМЕСТИТЬ ВТ_СотрудникиПериоды
ИЗ
    Справочник.Контрагенты КАК Контрагенты
ГДЕ
    НЕ Контрагенты.ЭтоГруппа
    И Контрагенты.Сотрудник <> ЗНАЧЕНИЕ(Справочник.Сотрудники.ПустаяСсылка)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    МИНИМУМ(ВТ_СотрудникиПериоды.ДатаНачала) КАК МинДатаНачала,
    МАКСИМУМ(ВТ_СотрудникиПериоды.ДатаОкончания) КАК МаксДатаОкончания
ПОМЕСТИТЬ ВТ_ГраницыПериода
ИЗ
    ВТ_СотрудникиПериоды КАК ВТ_СотрудникиПериоды
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Движения.Контрагент КАК Контрагент,
    Движения.Период КАК Период,
    Движения.СуммаПриход КАК СуммаПриход,
    Движения.СуммаРасход КАК СуммаРасход
ПОМЕСТИТЬ ВТ_ДвиженияФильтр
ИЗ
    РегистрНакопления.Взаиморасчеты КАК Движения
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ГраницыПериода КАК Границы
        ПО Движения.Период МЕЖДУ Границы.МинДатаНачала И Границы.МаксДатаОкончания
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Сотрудник КАК Сотрудник,
    ВТ.ДатаНачала КАК ДатаНачала,
    ВТ.ДатаОкончания КАК ДатаОкончания,
    СУММА(ЕСТЬNULL(Движения.СуммаПриход, 0)) КАК Приход,
    СУММА(ЕСТЬNULL(Движения.СуммаРасход, 0)) КАК Расход
ИЗ
    ВТ_СотрудникиПериоды КАК ВТ
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДвиженияФильтр КАК Движения
        ПО ВТ.Контрагент = Движения.Контрагент
        И Движения.Период МЕЖДУ ВТ.ДатаНачала И ВТ.ДатаОкончания
СГРУППИРОВАТЬ ПО
    ВТ.Сотрудник,
    ВТ.ДатаНачала,
    ВТ.ДатаОкончания

Обратим внимание: если реквизит Контрагент отсутствует в измерениях регистра и хранится только в реквизитах шапки документов, перед соединением потребуется явно ограничить типы регистраторов через оператор ВЫРАЗИТЬ.

Вариант 2. Использование связанных наборов данных в СКД

Рассмотрим альтернативную возможность, встроенную непосредственно в механизм Системы компоновки данных — связывание наборов данных (родительского и дочернего).

Разберем по шагам настройку схемы компоновки данных:

  1. Первый набор данных (Запрос). Выбирает список сотрудников, их контрагентов и рассчитывает поле ДатаНачала по формуле интервала.
  2. Второй набор данных (Запрос). Выбирает обороты из виртуальной таблицы РегистрНакопления.Взаиморасчеты.Обороты(&НачалоПериода, &КонецПериода, , ). В этом запросе используются стандартные скалярные параметры.
  3. Связи наборов данных. Во вкладке Связи наборов данных настраиваем связь первого набора со вторым. В качестве параметров связи передаем значение вычисленного поля ДатаНачала из первого набора в параметр &НачалоПериода подчиненного набора.

Проанализируем плюсы и минусы такого подхода. Главное преимущество — использование стандартной виртуальной таблицы оборотов, которая обращается к таблице итогов регистра. Однако у этого способа есть критический недостаток: платформа будет выполнять запрос дочернего набора отдельно для каждой строки родительского набора (эффект «N+1 запросов»). Если сотрудников в базе десятки или сотни, формирование отчета может занять длительное время. Этот вариант рекомендуется использовать только на небольших объемах данных.

Вариант 3. Использование набора данных «Объект»

В ситуациях, когда логика определения взаимосвязи документов, контрагентов и менеджеров слишком запутана (например, менеджер берется из сложной цепочки подчиненных документов или периодических регистров сведений), оптимальным решением становится использование набора данных типа Объект.

В этом случае в модуле объекта отчета (в процедуре ПриКомпоновкеРезультата) мы программно заполняем таблицу значений с колонками Сотрудник, ДатаНачала, Приход, Расход и передаем её в СКД через структуру внешних наборов данных. Это обеспечивает максимальную гибкость и исключает создание громоздких неоптимальных запросов на уровне СУБД.

← На главную