Как составить запрос для получения истории изменений зарплаты сотрудников без дублей?

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

При разработке отчетов по истории начислений в 1С программисты часто сталкиваются с проблемой "размножения" строк. Это происходит, когда текущая запись регистра сведений соединяется не с одной следующей по хронологии записью, а со всеми последующими записями сразу. В результате вместо четкой цепочки изменений (например, было 10 000, стало 15 000) мы получаем избыточный набор данных, где первая зарплата сопоставляется со всеми изменениями за несколько лет.

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

Разбор проблемы: почему появляются дубли?

Проанализируем ситуацию на примере кода из обсуждения. Типичная ошибка заключается в использовании условия соединения ВТПланТекущая.Период < ПлановыеНачисленияРаботников.Период. Без дополнительных ограничений это условие заставляет систему соединять запись от 2017 года с записями за 2018, 2019 и все последующие года. Чтобы получить только одно, следующее изменение, нам нужно найти минимальную дату среди всех дат, которые больше текущей.

Метод трех временных таблиц

Рассмотрим самый надежный и универсальный способ решения этой задачи, который работает на любых версиях платформы 1С 8. Этот метод заключается в последовательном формировании структуры данных.

Шаг 1: Подготовка исходных данных. Сначала мы выбираем все значимые записи из регистра РегистрСведений.ПлановыеНачисленияРаботников во временную таблицу. Это позволит нам обращаться к данным несколько раз без повторного сканирования всего регистра.


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

Шаг 2: Поиск ближайшего периода изменения. Теперь проанализируем ситуацию и найдем для каждой записи дату "следующего" события. Для этого мы соединяем таблицу саму с собой и используем агрегатную функцию МИНИМУМ.


ВЫБРАТЬ
    Т1.ФизЛицо,
    Т1.ВидРасчета,
    Т1.Период КАК ПериодСтарый,
    МИНИМУМ(Т2.Период) КАК ПериодНовый
ПОМЕСТИТЬ ВТ_СтыкПериодов
ИЗ
    ВТ_ВсеЗаписи КАК Т1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ВсеЗаписи КАК Т2
        ПО Т1.ФизЛицо = Т2.ФизЛицо
            И Т1.ВидРасчета = Т2.ВидРасчета
            И Т1.Период < Т2.Период
            И Т1.Результат <> Т2.Результат
СГРУППИРОВАТЬ ПО
    Т1.ФизЛицо,
    Т1.ВидРасчета,
    Т1.Период
;

Важный нюанс: условие Т1.Результат <> Т2.Результат позволяет нам игнорировать кадровые документы, которые не изменили сумму начисления (например, перевод в другой отдел без смены оклада), если это требуется по условию задачи.

Шаг 3: Соединение данных для итогового результата. Выясним окончательные значения, соединив найденные пары периодов с исходной таблицей записей дважды: один раз для получения данных "До", второй — для данных "После".


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

Использование оконных функций (для платформы 8.3.20+)

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

Рассмотрим пример такого подхода:


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

Этот метод работает в разы быстрее на больших объемах данных, так как СУБД выполняет обход таблицы один раз, сразу вычисляя значения для следующего периода.

Особенности работы с регистрами начислений

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

  1. Регистратор и МоментВремени. Если в одну секунду введены два документа (например, "Кадровый перевод" и "Изменение оплаты"), обычное сравнение по Периоду может выдать неверный результат. Проанализируем ситуацию: в таких случаях лучше использовать МоментВремени или добавлять в условия соединения сравнение по ссылке Регистратор.
  2. Прекращение начислений. Когда начисление прекращается, в регистр может записываться значение 0 или просто отсутствовать новая запись. В итоговом отчете важно решить, нужно ли показывать строку "Было 10 000 — Стало 0". Если да, используйте ЛЕВОЕ СОЕДИНЕНИЕ при поиске нового значения.
  3. Виртуальные таблицы. Для получения только последнего изменения иногда эффективнее использовать СрезПоследних с условием на период, но для построения полной истории за большой интервал времени (как в нашем случае) описанный выше метод с временными таблицами остается стандартом.

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

← На главную