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