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