Как оптимизировать запрос к регистру «Данные табельного учета рабочего времени сотрудников» в ЗУП 3.1?

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

В процессе разработки в конфигурации 1С:Зарплата и управление персоналом 3.1 программисты часто сталкиваются с проблемой низкой производительности запросов к регистру накопления ДанныеТабельногоУчетаРабочегоВремениСотрудников. Этот регистр является одним из самых объемных, так как хранит подробную информацию о времени за каждый день. Попробуем разобраться, почему типовые запросы могут работать медленно и как их правильно оптимизировать, опираясь на экспертный опыт.

Анализ проблемы: почему запрос «тормозит»?

Рассмотрим типичный пример запроса, который вызывает проблемы с производительностью. Основная ошибка заключается в использовании разыменования ссылочных полей через точку. Проанализируем исходный код:


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

В данном фрагменте мы видим несколько «критических точек», которые заставляют СУБД выполнять огромный объем лишней работы:

  1. Обращение через точку к реквизитам регистратора: Конструкция Регистратор.Подразделение.Код превращается в SQL-запросе в два дополнительных LEFT JOIN. Если в регистре миллионы записей, система будет пытаться соединить таблицу регистра с таблицей документов, а затем с таблицей справочника подразделений для каждой строки.
  2. Фильтр по ИНН: Условие Регистратор.Организация.ИНН = &ИНН — это еще более тяжелое соединение. СУБД не может эффективно использовать индексы самого регистра, так как ей нужно сначала «дотянуться» до ИНН в справочнике организаций.
  3. Сложные условия связи: Соединение по строковым кодам (ПодразделениеКод) всегда работает медленнее, чем по уникальным идентификаторам (ссылкам).

Шаг 1. Исключаем неявные соединения (обращения через точку)

Первое правило оптимизации в 1С: никогда не использовать обращения через точку в условиях ГДЕ и связях ПО, если речь идет о высоконагруженных таблицах. Проанализируем ситуацию: нам нужно отфильтровать данные по организации. Вместо того чтобы передавать в запрос ИНН, мы должны сначала найти ссылку на организацию в коде 1С и передать её как параметр.

Рассмотрим, как это реализовать:


// В коде 1С перед выполнением запроса
ОрганизацияСсылка = Справочники.Организации.НайтиПоРеквизиту("ИНН", ЗначениеИНН);
Запрос.УстановитьПараметр("Организация", ОрганизацияСсылка);

В самом запросе теперь мы сможем использовать прямое сравнение. Более того, в регистре ДанныеТабельногоУчетаРабочегоВремениСотрудников обычно присутствует измерение Организация. Использование измерения напрямую — залог успеха.

Шаг 2. Правильная работа с временными таблицами и индексами

Для ускорения работы необходимо подготовить данные во временных таблицах. Как отмечают эксперты, важно индексировать поля, по которым будет происходить соединение. Рассмотрим алгоритм:

  1. Выберем во временную таблицу ВТ_Параметры необходимые ссылки на сотрудников и подразделения, уже найденные по их кодам или другим признакам.
  2. Обязательно добавим ИНДЕКСИРОВАТЬ ПО для ключевых полей соединения.

Важный нюанс: индексация временной таблицы оправдана, если в ней содержится более 50–100 записей. Если записей всего несколько, затраты на создание индекса могут превысить выгоду от его использования. Однако в задачах расчета зарплаты данных обычно много, поэтому индексация — наш лучший друг.

Шаг 3. Оптимизация выбора регистратора

Если нам нужны только документы типа ТабельУчетаРабочегоВремени, можно пойти двумя путями. Либо фильтровать по типу регистратора, либо использовать соединение с физической таблицей самого документа. Посмотрим на оптимизированный подход:


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

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

В данном примере мы сначала максимально сужаем выборку документов, используя индексы таблицы документа, и только потом соединяем её с «тяжелым» регистром по полю Регистратор.

Шаг 4. Регистратор против Регистр: что выбрать?

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

Тем не менее, если мы работаем с данными о времени, рекомендуется:

Шаг 5. Анализ плана запроса

Выясним причину торможения с помощью технического анализа. В сложных случаях недостаточно просто переписать код — нужно понимать, как его «видит» СУБД. Посмотрим на план выполнения запроса (Query Plan). Основные моменты, на которые стоит обратить внимание:

Итоговый вариант оптимизированного запроса

С учетом всех рекомендаций, разберем структуру идеального запроса для решения задачи автора:


// 1. Подготавливаем параметры (Организация, Списки ссылок) вне запроса
// 2. Формируем запрос

ВЫБРАТЬ
    ВТ_Лица.СотрудникФизическоеЛицо,
    ВТ_Лица.ПодразделениеСсылка,
    ВТ_Лица.ПериодРегистрации
ПОМЕСТИТЬ ВТ_Фильтр
ИЗ
    &ТЗ_ВходныеДанные КАК ВТ_Лица
ИНДЕКСИРОВАТЬ ПО
    СотрудникФизическоеЛицо
;

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

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

← На главную