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