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

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

В практике разработки на платформе 1С часто возникает нетривиальная задача: необходимо получить состояние объекта не на текущий момент и не на фиксированную дату, а на конец того дня, когда в системе впервые появилась запись об этом объекте. Стандартная виртуальная таблица СрезПоследних здесь не помогает напрямую, так как она принимает на вход одну дату для всего запроса, а в нашей задаче для каждого «Заказа» или иного измерения дата «дебютного» дня будет своей.

Рассмотрим ситуацию подробнее. Допустим, по Заказу №1 первая запись в регистре появилась 1 марта. В течение этого дня значение ресурса менялось трижды. Нам нужно получить именно то значение, которое было актуально на последнюю секунду 1 марта. При этом по Заказу №2 первая запись могла появиться 15 марта, и нас будет интересовать состояние на конец 15 марта. В этой статье мы разберем по шагам, как построить эффективный запрос для решения этой задачи, минимизировать нагрузку на сервер и избежать типичных ошибок при работе с большими объемами данных.

Разбор алгоритма решения задачи

Для реализации такой логики нам потребуется использовать механизм временных таблиц. Мы не можем просто соединить таблицы «в лоб», так как это приведет к избыточному сканированию физических таблиц регистра. Разделим процесс на логические этапы, которые позволят нам постепенно сузить выборку данных.

Шаг 1: Определение дня первой регистрации

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

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


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

В этой таблице мы получаем список уникальных заказов и момент времени, ограничивающий их первый день жизни в системе. Мы используем ИНДЕКСИРОВАТЬ ПО, чтобы последующие соединения работали максимально быстро.

Шаг 2: Получение всех записей за первый день

Теперь нам нужно отобрать все записи из регистра, которые попадают в интервал от начала времен до конца нашего «первого дня» для каждого заказа. Мы объединяем физическую таблицу регистра с нашей временной таблицей ВТ_ПервыеДни.


ВЫБРАТЬ
    Регистр.Заказ КАК Заказ,
    Регистр.Период КАК Период
ПОМЕСТИТЬ ВТ_ВсеЗаписиПервогоДня
ИЗ
    РегистрСведений.ИмяРегистра КАК Регистр
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПервыеДни КАК ПервыеДни
    ПО Регистр.Заказ = ПервыеДни.Заказ
    И Регистр.Период <= ПервыеДни.ДатаРегистрацииКонецДня
ИНДЕКСИРОВАТЬ ПО
    Заказ,
    Период
;

На этом этапе мы отсекли все записи, которые были сделаны в последующие дни. Однако в рамках первого дня у нас все еще может быть несколько записей. Нам нужно найти самую позднюю из них.

Шаг 3: Поиск момента времени последней записи первого дня

Используя полученную таблицу ВТ_ВсеЗаписиПервогоДня, мы находим максимальный период для каждого заказа. Это и будет момент «среза последних» на конец первого дня.


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

Шаг 4: Получение конечных значений ресурсов

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


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

Важные технические нюансы и оптимизация

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

1. Проблема идентичных периодов

Если в вашем регистре сведений периодичность установлена в значение Секунда, существует вероятность, что в одну и ту же секунду по одному заказу будет внесено две записи. В этом случае МАКСИМУМ(Период) вернет две строки при соединении с физической таблицей. Чтобы этого избежать, рекомендуется в соединениях использовать не только Период, но и Регистратор или НомерСтроки. В идеале для таких задач лучше использовать Момент времени, который гарантирует уникальность каждой записи в хронологическом порядке.

2. Использование индексов

Как показывает практика, добавление индексации во временные таблицы (ИНДЕКСИРОВАТЬ ПО) может ускорить выполнение запроса в десятки раз, особенно если в регистре накоплены сотни тысяч записей. Индексировать следует те поля, по которым происходит связь: Заказ и Период.

3. Оптимизация объема данных

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

4. Альтернатива через кортежи

В некоторых случаях, вместо последовательного соединения трех-четырех временных таблиц, можно использовать конструкцию с оператором В и вложенным запросом. Рассмотрим, как это выглядит:


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

Этот подход позволяет оптимизатору SQL-сервера построить более эффективный план запроса через Semi-Join, что иногда дает выигрыш в производительности на СУБД типа PostgreSQL или MS SQL Server.

Выводы

Мы проанализировали ситуацию, когда стандартный срез последних не справляется с бизнес-логикой. Реализовав пошаговый алгоритм с использованием временных таблиц, мы получили надежный инструмент для анализа состояния объектов на дату их «входа» в систему. Помните, что ключевым фактором успеха здесь является правильная работа с границами периодов (использование КОНЕЦПЕРИОДА) и обязательная индексация промежуточных результатов. Такой подход обеспечит стабильную работу даже при значительном росте базы данных.

← На главную