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

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

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

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

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

Рассмотрим типичную ошибку. Разработчик пытается выполнить ЛЕВОЕ СОЕДИНЕНИЕ документа с виртуальной таблицей РегистрСведений.РаботникиОрганизаций.СрезПоследних. В условии связи указывается Документ.Сотрудник = Регистр.Сотрудник. При этом параметр «Период» виртуальной таблицы либо не заполнен, либо заполнен одной датой.

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

Решение проблемы: Метод поиска максимального периода

Для корректного решения мы откажемся от виртуальной таблицы СрезПоследних и обратимся к физической таблице регистра. Наша стратегия будет состоять из трех последовательных шагов. Проанализируем этот алгоритм подробнее.

Шаг 1: Подготовка исходных данных во временную таблицу

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

Рассмотрим пример формирования первой временной таблицы:


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

Шаг 2: Поиск актуального периода в регистре

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

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


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

Важный нюанс: Мы обязательно используем группировку, чтобы для каждой пары «Сотрудник — Дата из документа» получить только одну строку с максимальной датой из регистра.

Шаг 3: Получение ресурсов (должность, подразделение)

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


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

Как избежать дублей (Проблема записей в одну секунду)

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

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

Особенности для современных конфигураций (ЗУП 3.1)

Если вы работаете в современной конфигурации на базе 1С:ЗУП 3.1 или 1С:ERP, архитектура хранения кадровых данных значительно изменилась. Прямые запросы к регистрам вроде РаботникиОрганизаций больше не рекомендуются, так как данные могут быть распределены по разным таблицам или требовать сложного анализа состояний.

В таких случаях лучше использовать программный интерфейс (API), предоставляемый разработчиками фирмы «1С». Например, вызов функции КадровыйУчет.СоздатьВТКадровыеДанныеСотрудников. Вы передаете массив сотрудников и таблицу нужных дат, а система сама формирует временную таблицу со всеми актуальными данными. Это избавляет вас от необходимости вручную прописывать логику поиска среза последних и обработки дублей.

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

  1. Никогда не соединяйте виртуальные таблицы СрезПоследних с другими таблицами по условию периода внутри запроса — это ведет к неверным результатам или пустым значениям.
  2. Используйте метод «Трёх шагов»: подготовить данные -> найти МАКСИМУМ(Период) в физической таблице -> получить ресурсы по найденному периоду.
  3. Всегда помните о возможности нескольких записей в одну секунду и используйте дополнительные идентификаторы (регистратор, номер строки) для обеспечения уникальности. Такой подход с использованием произвольных полей поиска реализован в инструменте для переноса данных между похожими конфигурациями 1С, что предотвращает появление дублей.
  4. В современных конфигурациях отдавайте предпочтение типовым общим модулям кадрового учета для получения данных. При масштабном переходе на новую архитектуру эти задачи решает готовый перенос данных УПП 1.3 — ЗУП 3, учитывающий специфику регистров в разных редакциях программы.

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

← На главную