В процессе разработки в конфигурации 1С:Зарплата и управление персоналом 3.1 программисты часто сталкиваются с проблемой низкой производительности запросов к регистру накопления ДанныеТабельногоУчетаРабочегоВремениСотрудников. Этот регистр является одним из самых объемных, так как хранит подробную информацию о времени за каждый день. Попробуем разобраться, почему типовые запросы могут работать медленно и как их правильно оптимизировать, опираясь на экспертный опыт.
Рассмотрим типичный пример запроса, который вызывает проблемы с производительностью. Основная ошибка заключается в использовании разыменования ссылочных полей через точку. Проанализируем исходный код:
ВЫБРАТЬ
ВЫРАЗИТЬ(ДанныеТабельногоУчета.Регистратор КАК Документ.ТабельУчетаРабочегоВремени) КАК Регистратор
ИЗ
ВТФизичискиеЛицаПодразделения КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК ДанныеТабельногоУчета
ПО
ВТ.СотрудникФизическоеЛицо = ДанныеТабельногоУчета.Сотрудник.ФизическоеЛицо
И ВТ.ПодразделениеКод = ДанныеТабельногоУчета.Регистратор.Подразделение.Код
ГДЕ
ДанныеТабельногоУчета.Регистратор.Организация.ИНН = &ИНН
В данном фрагменте мы видим несколько «критических точек», которые заставляют СУБД выполнять огромный объем лишней работы:
Регистратор.Подразделение.Код превращается в SQL-запросе в два дополнительных LEFT JOIN. Если в регистре миллионы записей, система будет пытаться соединить таблицу регистра с таблицей документов, а затем с таблицей справочника подразделений для каждой строки.Регистратор.Организация.ИНН = &ИНН — это еще более тяжелое соединение. СУБД не может эффективно использовать индексы самого регистра, так как ей нужно сначала «дотянуться» до ИНН в справочнике организаций.ПодразделениеКод) всегда работает медленнее, чем по уникальным идентификаторам (ссылкам).Первое правило оптимизации в 1С: никогда не использовать обращения через точку в условиях ГДЕ и связях ПО, если речь идет о высоконагруженных таблицах. Проанализируем ситуацию: нам нужно отфильтровать данные по организации. Вместо того чтобы передавать в запрос ИНН, мы должны сначала найти ссылку на организацию в коде 1С и передать её как параметр.
Рассмотрим, как это реализовать:
// В коде 1С перед выполнением запроса
ОрганизацияСсылка = Справочники.Организации.НайтиПоРеквизиту("ИНН", ЗначениеИНН);
Запрос.УстановитьПараметр("Организация", ОрганизацияСсылка);
В самом запросе теперь мы сможем использовать прямое сравнение. Более того, в регистре ДанныеТабельногоУчетаРабочегоВремениСотрудников обычно присутствует измерение Организация. Использование измерения напрямую — залог успеха.
Для ускорения работы необходимо подготовить данные во временных таблицах. Как отмечают эксперты, важно индексировать поля, по которым будет происходить соединение. Рассмотрим алгоритм:
ВТ_Параметры необходимые ссылки на сотрудников и подразделения, уже найденные по их кодам или другим признакам.ИНДЕКСИРОВАТЬ ПО для ключевых полей соединения.Важный нюанс: индексация временной таблицы оправдана, если в ней содержится более 50–100 записей. Если записей всего несколько, затраты на создание индекса могут превысить выгоду от его использования. Однако в задачах расчета зарплаты данных обычно много, поэтому индексация — наш лучший друг.
Если нам нужны только документы типа ТабельУчетаРабочегоВремени, можно пойти двумя путями. Либо фильтровать по типу регистратора, либо использовать соединение с физической таблицей самого документа. Посмотрим на оптимизированный подход:
ВЫБРАТЬ
Табель.Ссылка КАК Регистратор
ИЗ
Документ.ТабельУчетаРабочегоВремени КАК Табель
ГДЕ
Табель.Проведен
И Табель.Организация = &Организация
И Табель.ПериодРегистрации = &ПериодРегистрации
И Табель.Подразделение В (&СписокПодразделений)
ПОМЕСТИТЬ ВТ_НужныеТабели
ИНДЕКСИРОВАТЬ ПО Ссылка
;
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТ_НужныеТабели.Регистратор
ИЗ
ВТ_НужныеТабели КАК ВТ_НужныеТабели
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК ДанныеРегистра
ПО
ВТ_НужныеТабели.Регистратор = ДанныеРегистра.Регистратор
И ДанныеРегистра.Организация = &Организация
В данном примере мы сначала максимально сужаем выборку документов, используя индексы таблицы документа, и только потом соединяем её с «тяжелым» регистром по полю Регистратор.
Существует мнение, что обращение напрямую к таблицам документов — это ошибка, так как достоверные данные содержатся только в регистрах. Проанализируем эту ситуацию. С точки зрения архитектуры 1С, регистры накопления действительно являются первоисточником для расчетов. Однако для целей получения списка документов (как в исходном вопросе автора), соединение с таблицей документа может быть оправдано, если оно помогает СУБД быстрее отсечь ненужные записи.
Тем не менее, если мы работаем с данными о времени, рекомендуется:
Сотрудник, Организация и Период самого регистра.Регистратор.Реквизит.КадроваяИсторияСотрудников во временную таблицу и соединять по Сотруднику.Выясним причину торможения с помощью технического анализа. В сложных случаях недостаточно просто переписать код — нужно понимать, как его «видит» СУБД. Посмотрим на план выполнения запроса (Query Plan). Основные моменты, на которые стоит обратить внимание:
С учетом всех рекомендаций, разберем структуру идеального запроса для решения задачи автора:
// 1. Подготавливаем параметры (Организация, Списки ссылок) вне запроса
// 2. Формируем запрос
ВЫБРАТЬ
ВТ_Лица.СотрудникФизическоеЛицо,
ВТ_Лица.ПодразделениеСсылка,
ВТ_Лица.ПериодРегистрации
ПОМЕСТИТЬ ВТ_Фильтр
ИЗ
&ТЗ_ВходныеДанные КАК ВТ_Лица
ИНДЕКСИРОВАТЬ ПО
СотрудникФизическоеЛицо
;
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВЫРАЗИТЬ(ДанныеРН.Регистратор КАК Документ.ТабельУчетаРабочегоВремени) КАК Регистратор
ИЗ
РегистрНакопления.ДанныеТабельногоУчетаРабочегоВремениСотрудников КАК ДанныеРН
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
ВТ_Фильтр КАК Фильтр
ПО
ДанныеРН.Сотрудник.ФизическоеЛицо = Фильтр.СотрудникФизическоеЛицо
И ДанныеРН.ПериодРегистрации = Фильтр.ПериодРегистрации
// Используем измерение Организация напрямую, если оно есть в регистре
ГДЕ
ДанныеРН.Организация = &Организация
И ДанныеРН.Регистратор ССЫЛКА Документ.ТабельУчетаРабочегоВремени
Подведем итог: оптимизация запросов в ЗУП 3.1 сводится к минимизации «точек», предварительной подготовке ссылок вместо поиска по строковым реквизитам и грамотному использованию индексов временных таблиц. Применяя эти правила, мы можем сократить время выполнения запроса с нескольких минут до долей секунды.