При разработке собственных отчетов, вариантов Универсального отчета или внешних выборок в конфигурации 1С:Зарплата и Управление Персоналом разработчики часто обращаются напрямую к интервальным регистрам сведений (например, КадроваяИсторияСотрудниковИнтервальный). Такой подход кажется привлекательным благодаря высокой скорости выборки среза данных без использования тяжелых виртуальных таблиц сведений. Однако при попытке написать простое условие на дату многие сталкиваются с некорректным поведением запроса: выбираются не те записи или пропускаются актуальные интервалы. Разберем по шагам, как устроены интервальные регистры, почему возникают ошибки и как правильно составить условие выборки.
Для понимания логики работы запроса проанализируем внутреннее устройство интервальных регистров в 1С:ЗУП:
ДатаНачала редко равен строго 00:00:00, а ДатаОкончания может отличаться от стандартного конца дня 23:59:59.ДатаОкончания не остается пустым, а заполняется максимальной технологической датой платформы — 31.12.3999 23:59:59.ОбновитьИнтервальныйРегистрСведений) при записи базовых периодических регистров. При программных сбоях или нехронологическом проведении документов данные интервалов могут рассинхронизироваться.Выясним причину типичной ошибки: если написать простое условие с оператором МЕЖДУ или сравнить дату начала без учета времени, запрос на определенную дату возьмет не актуальную, а предыдущую запись интервала. Чтобы гарантированно захватить нужный интервал, целевую дату необходимо приводить к концу дня.
Посмотрим на пример правильного условия для выборки кадрового среза на конкретную дату:
ВЫБРАТЬ
Регистр.Сотрудник,
Регистр.Подразделение,
Регистр.Должность,
Регистр.ВидСобытия
ИЗ
РегистрСведений.КадроваяИсторияСотрудниковИнтервальный КАК Регистр
ГДЕ
Регистр.ДатаНачала <= КОНЕЦПЕРИОДА(&Период, ДЕНЬ)
И Регистр.ДатаОкончания >= КОНЕЦПЕРИОДА(&Период, ДЕНЬ)
И Регистр.ВидСобытия <> ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение)
И НЕ Регистр.ПустойИнтервал
Разберем подробнее, что делают эти условия:
КОНЕЦПЕРИОДА(&Период, ДЕНЬ) гарантирует, что даже если в поле ДатаНачала записано смещение на несколько секунд вперед от начала дня (например, 01.08.2026 00:00:01), условие ДатаНачала <= 01.08.2026 23:59:59 успешно выполнится.ДатаОкончания >= КОНЕЦПЕРИОДА(&Период, ДЕНЬ) гарантирует, что срез попадет внутрь действующего интервала, включая открытые записи с датой 3999-12-31.ВидСобытия <> ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение) исключает сотрудников, у которых интервал действует после даты увольнения.НЕ Регистр.ПустойИнтервал (если реквизит предусмотрен в структуре регистра) отсекает служебные фиктивные интервалы.Если в вашем отчете параметр периода может быть не заполнен (например, требуется получить срез на текущий момент при пустом значении), дополним условие обработкой пустой даты:
ГДЕ
Регистр.ДатаНачала <= ВЫБОР
КОГДА &Период = ДАТАВРЕМЯ(1, 1, 1) ТОГДА ДАТАВРЕМЯ(3999, 12, 31, 23, 59, 59)
ИНАЧЕ КОНЕЦПЕРИОДА(&Период, ДЕНЬ)
КОНЕЦ
И Регистр.ДатаОкончания >= ВЫБОР
КОГДА &Период = ДАТАВРЕМЯ(1, 1, 1) ТОГДА ДАТАВРЕМЯ(3999, 12, 31, 23, 59, 59)
ИНАЧЕ КОНЕЦПЕРИОДА(&Период, ДЕНЬ)
КОНЕЦ
Если перед нами стоит задача отобрать кадровые изменения или состояния сотрудников не на одну дату, а за произвольный период времени, используем классическое условие пересечения двух отрезков времени:
ГДЕ
Регистр.ДатаНачала <= КОНЕЦПЕРИОДА(&ДатаОкончания, ДЕНЬ)
И Регистр.ДатаОкончания >= НАЧАЛОПЕРИОДА(&ДатаНачала, ДЕНЬ)
Такая конструкция вернет все записи регистра, действие которых хотя бы частично пересекается с выбранным временным диапазоном.
Прямое чтение интервальных таблиц часто необходимо для простых настроек Универсального отчета, однако разработчики конфигурации считают такой подход низкоуровневым и не гарантируют неизменность поведения вспомогательных таблиц в будущих версиях. Рассмотрим более надежные и штатные альтернативы:
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&Период). Этот способ полностью поддерживается Универсальным отчетом и компоновкой данных без ручной обработки секунд и миллисекунд. На небольших базах разница в производительности практически незаметна.КадровыйУчет:
// Получение кадровых данных на дату в виде таблицы
КадровыеДанные = КадровыйУчет.КадровыеДанныеСотрудников(Истина, Сотрудники, "Подразделение,Должность", &Период);
// Получение списка работающих сотрудников
ТаблицаСотрудников = КадровыйУчет.СотрудникиОрганизации(Организация, &Период);
Представления_ТаблицаРегистра_КадроваяИсторияСотрудников с вызовом процедуры ЗарплатаКадрыОбщиеНаборыДанных.ЗаполнитьОбщиеИсточникиДанныхОтчета(ЭтотОбъект) перед компоновкой данных. Это стандартный архитектурный подход ЗУП 3 для тиражных отчетов.