Как в СКД получить заказы с актуальным статусом при наличии нескольких измерений в регистре сведений?

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

При разработке отчетов в системе компоновки данных (СКД) часто возникает задача: отобрать документы, находящиеся в определенном статусе на текущий момент времени, но с условием, что переход в этот статус произошел в заданном интервале дат. На первый взгляд задача решается простым обращением к виртуальной таблице СрезПоследних. Однако, если регистр сведений имеет не одно, а несколько измерений (например, Заказ и ДокументОснование), стандартный срез начинает возвращать неактуальные данные. Разберем подробно, почему так происходит и как правильно составить запрос.

Анализ проблемы: почему стандартный срез последних возвращает «закрытые» заказы

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

Проанализируем типовой жизненный цикл заказа:

  1. Документ установки статуса согласовывает заказ. В регистр записывается строка: измерение Заказ = Заказ №1, измерение ДокументОснование = Согласование №1, ресурс Статус = Согласован.
  2. Позже заказ закрывается отдельным документом. В регистр добавляется новая строка: измерение Заказ = Заказ №1, измерение ДокументОснование = Закрытие №1, ресурс Статус = Закрыт.

Если мы запросим срез последних без отбора по конкретному основанию, платформа вернет обе записи, поскольку для пары (Заказ №1, Согласование №1) последним статусом остался «Согласован», а для пары (Заказ №1, Закрытие №1) — «Закрыт». Наложив условие ГДЕ Статус = &Согласован, мы отсечем закрывающую строку, но пропустим первую запись. В итоге в отчет ошибочно попадет заказ, который фактически уже закрыт.

Дополнительную сложность при работе в СКД вносит автозаполнение параметров виртуальных таблиц. Если оставить скобки среза пустыми СрезПоследних(), СКД может автоматически передать в параметр срез дату окончания отчета (&КонецПериода), в результате чего срез будет строиться не на текущий момент времени, а на историческую дату.

Способ 1. Получение среза через временные таблицы и группировку по заказу

Чтобы исключить влияние лишнего измерения ДокументОснование, выполним выборку максимальной даты изменения состояния строго в разрезе заказов. Для этого обратимся к физической таблице регистра, сгруппируем данные по измерению Заказ и соединим результат с записями регистра.

Разберем по шагам текст эффективного пакетного запроса:


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

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

Посмотрим, как работает этот алгоритм:

  1. В первой таблице ВТ_ПоследниеДаты мы определяем абсолютно последнюю точку изменения статуса по каждому заказу без учета оснований.
  2. Во втором запросе мы соединяем полученные даты с основной таблицей регистра и отбираем только те заказы, у которых эта самая последняя запись имеет статус &СтатусСогласован и дата ее установки укладывается в выбранный интервал отчета.

Способ 2. Запрос с вложенной таблицей среза

Если требуется решить задачу одним запросом без использования пакетного менеджера временных таблиц, можно применить соединение вложенного запроса к срезу последних:


ВЫБРАТЬ
    СтатусыЗаказов.Заказ КАК Заказ,
    СтатусыЗаказов.Период КАК ДатаСогласования,
    СтатусыЗаказов.Статус КАК Статус
ИЗ
    (ВЫБРАТЬ
        СЗ.Заказ КАК Заказ,
        МАКСИМУМ(СЗ.Период) КАК ДатаПоследнегоСостояния
    ИЗ
        РегистрСведений.СтатусыЗаказов.СрезПоследних(&ТекущаяДата, ) КАК СЗ
    СГРУППИРОВАТЬ ПО
        СЗ.Заказ) КАК ДатыПоследнихСостояний
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СтатусыЗаказов.СрезПоследних(&ТекущаяДата, ) КАК СтатусыЗаказов
        ПО ДатыПоследнихСостояний.Заказ = СтатусыЗаказов.Заказ
        И ДатыПоследнихСостояний.ДатаПоследнегоСостояния = СтатусыЗаказов.Период
ГДЕ
    СтатусыЗаказов.Статус = &СтатусСогласован
    И СтатусыЗаказов.Период МЕЖДУ &НачалоПериода И &КонецПериода

Обратим внимание: при использовании виртуальной таблицы в СКД крайне важно явно передавать параметр даты среза (например, &ТекущаяДата), чтобы механизм компоновки данных не подменил его значением границы периода отчета.

Архитектурные рекомендации по проектированию регистра

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

  1. Перенос поля в реквизиты: Если поле ДокументОснование носит справочный характер (служит для понимания, какой документ изменил статус), его следует сделать реквизитом, а не измерением регистра. В этом случае измерение Заказ останется единственным, и виртуальная таблица СрезПоследних будет штатно возвращать ровно одну актуальную запись на каждый заказ без сложных подзапросов.
  2. Использование механизма итогов: Для высоконагруженных периодических регистров сведений рекомендуется включать свойство Разрешить итоги: Срез последних. Это позволяет платформе поддерживать отдельную таблицу среза на уровне базы данных и существенно ускоряет выполнение запросов.
← На главную