При разработке на платформе 1С программисты часто сталкиваются с задачей получения данных из периодического регистра сведений не на текущий момент, а на определенные даты в прошлом. Ситуация усложняется, когда таких дат много — например, нам нужно получить подразделение или должность сотрудника на каждую дату формирования кадрового документа из списка. Стандартная виртуальная таблица СрезПоследних не позволяет передать в параметры поле из другой таблицы, поэтому приходится использовать механизмы запросов и временных таблиц.
В этой статье мы разберем, почему простой запрос с группировкой может не работать, и рассмотрим профессиональный алгоритм решения этой задачи.
Рассмотрим ситуацию, с которой столкнулся автор темы. В запросе выполняется соединение временной таблицы с датами и регистра сведений. Основная идея — найти максимальный период в регистре, который меньше или равен целевой дате. Однако, если в список полей СГРУППИРОВАТЬ ПО включить ресурс регистра (например, КостЦентр), запрос вернет все записи регистра, попадающие под условие Регистр.Период <= Дата, а не одну последнюю.
Проанализируем типичный ошибочный код:
ВЫБРАТЬ
ОбщиеДанные.Сотрудник КАК Сотрудник,
ОбщиеДанные.ДатаИзменения КАК ДатаИзменения,
КостЦентрФизЛица.КостЦентр КАК КостЦентр,
МАКСИМУМ(КостЦентрФизЛица.Период) КАК Период
ИЗ
ОбщиеДанные КАК ОбщиеДанные
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КостЦентрФизЛица КАК КостЦентрФизЛица
ПО ОбщиеДанные.Сотрудник = КостЦентрФизЛица.Сотрудник
И ОбщиеДанные.ДатаИзменения >= КостЦентрФизЛица.Период
СГРУППИРОВАТЬ ПО
ОбщиеДанные.Сотрудник,
ОбщиеДанные.ДатаИзменения,
КостЦентрФизЛица.Период, // ОШИБКА: Период не должен быть в группировке
КостЦентрФизЛица.КостЦентр // ОШИБКА: Ресурс разбивает записи на разные группы
Почему это не работает: Группировка в языке запросов 1С работает так же, как в SQL. Если значение в поле КостЦентр менялось со временем, то для каждой смены значения СУБД создаст отдельную строку. Функция МАКСИМУМ(Период) в этом случае просто вернет период конкретной записи внутри группы, и мы получим «дубли» строк на одну и ту же дату.
Для надежного получения данных используется трехшаговая схема с применением временных таблиц. Это позволяет четко разделить логику поиска нужного момента времени и получение самих значений.
Сначала мы должны определить для каждой пары «Объект + Целевая дата» тот самый единственный Период в регистре, который является актуальным. На этом этапе мы выбираем только ключевые поля и период, исключая ресурсы (такие как подразделение, сумма, склад и т.д.).
ВЫБРАТЬ
ВТ_Даты.Сотрудник КАК Сотрудник,
ВТ_Даты.ДатаСреза КАК ДатаСреза,
МАКСИМУМ(Регистр.Период) КАК ПериодДляСреза
ПОМЕСТИТЬ ВТ_МаксимальныеПериоды
ИЗ
ВТ_Даты КАК ВТ_Даты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КостЦентрФизЛица КАК Регистр
ПО ВТ_Даты.Сотрудник = Регистр.Сотрудник
И ВТ_Даты.ДатаСреза >= Регистр.Период
СГРУППИРОВАТЬ ПО
ВТ_Даты.Сотрудник,
ВТ_Даты.ДатаСреза
ИНДЕКСИРОВАТЬ ПО
Сотрудник,
ПериодДляСреза
;
Важный момент: Мы обязательно используем ИНДЕКСИРОВАТЬ ПО. Это критически важно для производительности последующего соединения, особенно если в таблице сотни или тысячи строк.
Теперь, когда у нас есть точные значения периодов, мы можем соединить нашу временную таблицу с самим регистром сведений. Соединение будет идти по условию «Равно», что работает максимально быстро.
ВЫБРАТЬ
ВТ_МаксПериоды.Сотрудник КАК Сотрудник,
ВТ_МаксПериоды.ДатаСреза КАК ДатаСреза,
РегистрДанные.КостЦентр КАК КостЦентр
ИЗ
ВТ_МаксимальныеПериоды КАК ВТ_МаксПериоды
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КостЦентрФизЛица КАК РегистрДанные
ПО ВТ_МаксПериоды.Сотрудник = РегистрДанные.Сотрудник
И ВТ_МаксПериоды.ПериодДляСреза = РегистрДанные.Период
Выясним причину возможных дублей, если периодичность регистра установлена в значение Позиция регистратора или в одну секунду было записано несколько движений. В этом случае одного МАКСИМУМ(Период) будет недостаточно, так как на одну секунду может прийтись две разные записи.
В таких ситуациях разберем дополнительные меры защиты:
МАКСИМУМ(Регистратор) или использовать связку с моментом времени.НомерСтроки (если оно есть в данном регистре).Проанализируем ситуацию с точки зрения нагрузки на сервер СУБД. Использование условия >= в соединении (неравенство) заставляет систему выполнять вложенные циклы. Чтобы запрос работал быстрее, придерживайтесь следующих правил:
Таким образом, мы выяснили, что основной ошибкой является попытка получить срез в одно действие через группировку по ресурсам. Использование временных таблиц для поэтапного нахождения максимального периода и последующего получения данных — это наиболее стабильный и быстрый способ решения задачи в 1С.