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

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

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

Метод 1: Определение границ периодов через левые соединения

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

Разберем по шагам реализацию этой логики в одном запросе:


ВЫБРАТЬ
    Таблица1.Сотрудник КАК Сотрудник,
    Таблица1.УсловияТруда КАК УсловияТруда,
    МИНИМУМ(Таблица1.Период) КАК ПериодНачало,
    Таблица2.Период КАК ПериодОкончание
ИЗ
    РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК Таблица1
    
    // Ищем отсутствие записи на предыдущий день (определение начала)
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК Таблица3
    ПО Таблица1.Сотрудник = Таблица3.Сотрудник
        И Таблица1.УсловияТруда = Таблица3.УсловияТруда
        И (Таблица3.Период = ДОБАВИТЬКДАТЕ(Таблица1.Период, ДЕНЬ, -1))
        
    // Ищем минимальную дату окончания, которая больше текущей даты начала
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК Таблица2
    ПО Таблица1.Сотрудник = Таблица2.Сотрудник
        И Таблица1.УсловияТруда = Таблица2.УсловияТруда
        И (Таблица2.Период В (
            ВЫБРАТЬ
                МИНИМУМ(Н.Период)
            ИЗ
                РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК Н
                ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК Н1
                ПО Н.Сотрудник = Н1.Сотрудник
                    И Н.УсловияТруда = Н1.УсловияТруда
                    И Н1.Период = ДОБАВИТЬКДАТЕ(Н.Период, ДЕНЬ, 1)
            ГДЕ
                Н.Период >= Таблица1.Период
                И Н.Сотрудник = Таблица1.Сотрудник
                И Н.УсловияТруда = Таблица1.УсловияТруда
                И Н1.Период ЕСТЬ NULL
        ))
ГДЕ
    НЕ Таблица1.УсловияТруда = ЗНАЧЕНИЕ(Справочник.УсловияТруда.ПустаяСсылка)
    И Таблица1.Период МЕЖДУ &ДатаНачала И &ДатаОкончания
    И Таблица3.Период ЕСТЬ NULL
СГРУППИРОВАТЬ ПО
    Таблица1.Сотрудник,
    Таблица1.УсловияТруда,
    Таблица2.Период

Важное замечание: Этот запрос эффективно работает, когда данные в табеле идут непрерывно (день за днем). Если в данных есть разрывы (например, выходные дни, по которым нет записей в регистре), условие ДОБАВИТЬКДАТЕ(..., ДЕНЬ, -1) может ошибочно посчитать каждый понедельник новой точкой начала. В таких случаях рекомендуется сначала подготовить временную таблицу с уникальными рабочими датами сотрудника.

Метод 2: Оптимизированный поиск изменений (Алгоритм «Островов»)

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

Рассмотрим алгоритм действий:

  1. Выбираем все записи во временную таблицу ВТ_ИсходныеДанные с добавлением уникального индекса.
  2. Создаем вторую временную таблицу ВТ_ПорядковыеНомера, где для каждой записи вычисляем количество предыдущих записей по этому сотруднику.
  3. Вычисляем ГруппировочнаяДата = ДОБАВИТЬКДАТЕ(Период, ДЕНЬ, -ПорядковыйНомер).
  4. Делаем итоговую выборку с СГРУППИРОВАТЬ ПО Сотрудник, УсловияТруда, ГруппировочнаяДата, используя МИНИМУМ(Период) и МАКСИМУМ(Период).

Метод 3: Программная обработка таблицы значений

Как справедливо отмечено в сообщениях 4 и 6, иногда запрос становится слишком громоздким и трудночитаемым. В этом случае эффективнее выгрузить данные в ТаблицаЗначений и обойти их циклом. Выясним причину популярности этого метода: он позволяет легко обрабатывать сложные условия «разрывов» и внутрисменных изменений.

Проанализируем пример логики на языке 1С:


// Предположим, ТЗ_Данные уже отсортирована по Сотруднику и Периоду
ТЗ_Результат = ТЗ_Данные.СкопироватьКолонки();
ТЗ_Результат.Колонки.Добавить("ДатаНачала");
ТЗ_Результат.Колонки.Добавить("ДатаОкончания");

ТекущийПериод = Неопределено;

Для Каждого Строка Из ТЗ_Данные Цикл
    Если ТекущийПериод = Неопределено 
        Или Строка.Сотрудник <> ТекущийПериод.Сотрудник 
        Или Строка.УсловияТруда <> ТекущийПериод.УсловияТруда
        Или Строка.Период <> ДобавитьКДате(ТекущийПериод.ДатаОкончания, "ДЕНЬ", 1) Тогда
        
        // Создаем новую строку периода
        ТекущийПериод = ТЗ_Результат.Добавить();
        ЗаполнитьЗначенияСвойств(ТекущийПериод, Строка);
        ТекущийПериод.ДатаНачала = Строка.Период;
        ТекущийПериод.ДатаОкончания = Строка.Период;
    Иначе
        // Продлеваем текущий период
        ТекущийПериод.ДатаОкончания = Строка.Период;
    КонецЕсли;
КонецЦикла;

Нюансы работы с регистром «Данные табельного учета»

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

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

← На главную