Как получить одну последнюю запись в регистре сведений для каждого объекта

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

В практике разработки на платформе 1С часто возникает ситуация, когда стандартная виртуальная таблица СрезПоследних возвращает «лишние» данные. Это происходит, если в регистре сведений, помимо основного аналитического разреза (например, ФизЛицо), существуют другие измерения (например, Организация или Сотрудник). Система возвращает последние записи для каждой уникальной комбинации всех измерений, в то время как разработчику часто требуется получить ровно одну строку — самую актуальную для конкретного человека. Разберем детально, как решить эту задачу максимально эффективно.

Причина возникновения проблемы «лишних» строк

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

Метод 1. Использование временной таблицы с максимальным периодом

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

Рассмотрим по шагам реализацию этого метода в языке запросов:

  1. Создадим первый запрос, в котором выберем измерение (ФизЛицо) и применим агрегатную функцию МАКСИМУМ к полю Период.
  2. Сгруппируем результат по ФизЛицу.
  3. Поместим этот результат во временную таблицу.
  4. Во втором запросе соединим эту временную таблицу с физической таблицей регистра или таблицей СрезПоследних по двум полям: ФизЛицо и Период.

Разберем пример кода для такого запроса:


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

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

Метод 2. Решение проблемы одинаковых периодов (Момент времени)

Иногда случается, что в пределах одной секунды (минимальная дискретность периода в 1С) по одному объекту вносятся две разные записи. В этом случае МАКСИМУМ(Период) вернет обе записи, и в итоговой таблице снова появятся дубли. Проанализируем ситуацию: как системе понять, какая запись была «позже», если время совпадает? Для этого используются дополнительные поля: Регистратор и НомерСтроки.

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

  1. Находим максимальный период.
  2. Для найденного периода находим максимальный номер строки или максимальную ссылку на регистратор.
  3. Соединяем по трем полям.

Метод 3. Параметризация виртуальной таблицы «СрезПоследних»

Часто программисты допускают ошибку, пытаясь фильтровать данные в секции ГДЕ уже после получения таблицы СрезПоследних. Выясним причину, почему это плохо: 1С сначала выполнит вложенный подзапрос для получения среза по всем измерениям, а затем отсечет лишнее. Это сильно бьет по производительности.

Правильный подход — передавать отборы напрямую в параметры виртуальной таблицы. Если мы точно знаем, по каким измерениям нам нужны данные, их нужно указать в параметрах:


ВЫБРАТЬ
    Срез.ФизЛицо,
    Срез.Должность
ИЗ
    РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&Период, ФизЛицо В (&МассивФизЛиц)) КАК Срез

Однако помните, что это не уберет дубли, если Сотрудник (другое измерение) не указан в отборе. Этот метод эффективен только в сочетании с Методом 1.

Особенности работы с периодичностью и индексами

Проанализируем технические нюансы, которые влияют на скорость работы таких запросов:

1. Индексация. Чтобы поиск по максимальной дате в разрезе ФизЛица работал быстро на сотнях тысяч записей, убедитесь, что в метаданных регистра у измерения ФизЛицо установлено свойство Индексировать. Если это измерение идет первым в списке — индекс создается автоматически. Это предотвратит полное сканирование таблицы (Full Scan) на уровне SQL-сервера.

2. Периодичность регистра. Если ваш регистр имеет периодичность «В пределах дня», время в поле Период всегда будет 00:00:00. В таких условиях использование МАКСИМУМ(Период) без связи с регистратором практически бесполезно, так как все записи за день будут считаться «одновременными».

Метод 4. Программное получение (Объектная модель)

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


Отбор = Новый Структура("ФизЛицо", ТекущееФизЛицо);
ПоследниеДанные = РегистрыСведений.КадроваяИсторияСотрудников.СрезПоследних(ТекущаяДата, Отбор);

Если ПоследниеДанные.Количество() > 0 Тогда
    // Если записей несколько из-за измерений, берем самую первую (нулевую)
    ПоследняяСтрока = ПоследниеДанные[0];
    Должность = ПоследняяСтрока.Должность;
КонецЕсли;

Важное примечание: данный метод возвращает ТаблицуЗначений. Если вы понимаете, что по одному физлицу может вернуться несколько строк (разные сотрудники), вам все равно придется программно обходить эту таблицу и выбирать нужную строку, руководствуясь бизнес-логикой (например, по признаку основного места работы).

Подведем итоги

Для корректного получения последней записи в запросе мы должны следовать правилу: сначала определяем «ключи» уникальности (Максимальный Период + ФизЛицо), а затем получаем данные. Не стоит забывать про производительность — всегда ограничивайте выборку во временных таблицах по нужным вам объектам через массив ссылок в параметрах. Использование только ВЫБРАТЬ ПЕРВЫЕ 1 с сортировкой допустимо только в том случае, если вам нужна одна запись на весь запрос, а не по каждому объекту из списка.

← На главную