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

Программист 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

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

Фундаментальный принцип: Срез в разрезе всех измерений

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

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

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

Параметры виртуальной таблицы против предложения ГДЕ

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

  1. Условие в параметрах виртуальной таблицы:

    Если вы пишете РегистрСведений.Имя.СрезПоследних(&Период, ВидСобытия <> &Увольнение), система сначала отфильтровывает все записи в физической таблице, где вид события — «Увольнение». И только среди оставшихся записей начинает искать максимальный период. В итоге, если последним событием было «Увольнение», запрос «проскочит» его и выдаст вам предыдущую запись (например, «Перемещение» или «Прием»), что введет вас в заблуждение, будто сотрудник все еще работает.

  2. Условие в предложении ГДЕ:

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

Посмотрим на пример правильного наложения фильтра, когда нам нужно исключить записи по конкретному значению ресурса:


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

Специфика ЗУП и интервальные регистры

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

В этих регистрах вместо поиска «последней записи» используется более эффективный отбор по попаданию даты в интервал:


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

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

Как сформировать «Срез» вручную по выбранным измерениям

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

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

  1. Создаем временную таблицу с максимальными периодами для ключевого измерения (например, Сотрудник).
  2. Соединяем эту временную таблицу с физической таблицей регистра по периоду и измерению.
  3. Получаем ресурсы из физической таблицы.

Пример реализации:


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

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

Внимание: Если в одну секунду (период) может быть внесено несколько записей по одному сотруднику, для однозначности придется использовать дополнительное поле — НомерСтроки (если регистр подчинен регистратору) или другие уникальные признаки.

Влияние версии платформы и СУБД

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

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

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

← На главную