При работе в конфигурациях на обычных формах, таких как 1С:Управление торговлей 10.3, разработчики часто сталкиваются с проблемой медленной прокрутки списков документов. Одной из наиболее типичных ситуаций является необходимость отображения актуального состояния заказа (например, есть ли по нему остатки в регистре накопления) непосредственно в форме списка. Если реализовать это «в лоб», интерфейс начинает «тормозить», а замер производительности показывает, что 90% времени тратится на один и тот же запрос к виртуальной таблице остатков.
В этой статье мы подробно разберем, почему возникают такие задержки, как правильно оптимизировать запрос к виртуальной таблице и какие настройки метаданных критически влияют на скорость работы системы.
Рассмотрим ситуацию: в списке заказов покупателей необходимо визуально выделить строки, по которым еще остались невыполненные обязательства. Для этого в событии ПриПолученииДанных выполняется следующий запрос:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВЫРАЗИТЬ(ЗаказыПокупателейОстатки.ЗаказПокупателя КАК Документ.ЗаказПокупателя) КАК ЗаказПокупателя
ИЗ
РегистрНакопления.ЗаказыПокупателей.Остатки(, ЗаказПокупателя В (&МассивДокументов)) КАК ЗаказыПокупателейОстатки
ГДЕ
ЗаказыПокупателейОстатки.КоличествоОстаток <> 0
Проблема заключается в том, что при каждом листании списка или обновлении экрана этот запрос выполняется заново для порции отображаемых документов. Если в базе данных много записей, а настройки регистра не оптимальны, система начинает выполнять полное сканирование таблиц вместо быстрого поиска по индексу.
Проанализируем структуру регистра накопления ЗаказыПокупателей. В системе 1С индексы для физических таблиц итогов строятся на основе измерений регистра. Важнейшее правило: порядок измерений в конфигураторе напрямую влияет на составной индекс в SQL Server.
Если измерение ЗаказПокупателя стоит в списке вторым или третьим, а в запросе не указаны значения для первых измерений (например, «Организация»), то SQL-сервер не сможет эффективно использовать индекс. Вместо операции Index Seek (поиск по индексу) будет выполнена операция Index Scan (просмотр всего индекса), что на больших объемах данных крайне медленно.
Для оптимизации выполним следующие действия:
ЗаказПокупателя свойство Индексировать.Как показала практика, после индексации измерения время выполнения запроса может сократиться с нескольких секунд до сотых долей секунды (в рассматриваемом случае с 2.7 сек до 0.18 сек), что делает работу пользователя комфортной.
Часто возникает спор: как эффективнее передавать список документов в условие виртуальной таблицы — через массив или через временную таблицу? Рассмотрим оба варианта.
Если документов в массиве немного (до 50-100 штук), использование конструкции ЗаказПокупателя В (&МассивДокументов) вполне допустимо. Однако при увеличении количества ссылок SQL-план запроса может стать неоптимальным. В этом случае рекомендуется использовать временную таблицу с индексацией:
// 1. Помещаем ссылки во временную таблицу
ВЫБРАТЬ Ссылка КАК Заказ ПОМЕСТИТЬ ВТ_Фильтр ИЗ Документ.ЗаказПокупателя ГДЕ Ссылка В (&МассивДокументов) ИНДЕКСИРОВАТЬ ПО Заказ;
// 2. Используем ВТ в параметрах виртуальной таблицы
ВЫБРАТЬ
ЗаказыПокупателейОстатки.ЗаказПокупателя
ИЗ
РегистрНакопления.ЗаказыПокупателей.Остатки(, ЗаказПокупателя В (ВЫБРАТЬ Заказ ИЗ ВТ_Фильтр)) КАК ЗаказыПокупателейОстатки
ГДЕ
ЗаказыПокупателейОстатки.КоличествоОстаток <> 0;
Проанализируем ситуацию: использование временной таблицы позволяет SQL-серверу более точно построить план соединения таблиц. Однако стоит помнить, что создание ВТ само по себе требует ресурсов, поэтому для маленьких порций данных в событии ПриПолученииДанных обычный массив зачастую работает быстрее.
Важно помнить разницу между параметрами виртуальной таблицы (условия в скобках) и предложением ГДЕ. Выясним причину, почему это важно:
В нашем случае условие по измерению ЗаказПокупателя обязательно должно быть внутри параметров виртуальной таблицы. А вот условие по ресурсу КоличествоОстаток <> 0 физически невозможно вынести в параметры, так как ресурсы не входят в индекс таблицы итогов. Поэтому оно остается в ГДЕ. Рассмотрим пример правильной структуры запроса:
ВЫБРАТЬ
Остатки.ЗаказПокупателя КАК ЗаказПокупателя
ИЗ
РегистрНакопления.ЗаказыПокупателей.Остатки( , ЗаказПокупателя В (&МассивДокументов)) КАК Остатки
ГДЕ
Остатки.КоличествоОстаток <> 0
Если технической оптимизации запроса недостаточно, проанализируем архитектурные подходы:
Соответствие, куда будут сохраняться уже полученные остатки для заказов. При прокрутке списка назад программа сначала будет проверять наличие данных в кэше, не обращаясь к базе данных.ЗаказЗакрыт (индексируемый). При полном выполнении заказа или его отмене регламентным заданием или при проведении документов этот флаг устанавливается в Истина. Тогда в списке можно будет проверять этот флаг, вообще не обращаясь к регистру остатков, что обеспечит мгновенную работу.Подведем итог: для оптимизации списков в УТ 10.3 первым делом проверьте индексацию измерений и их порядок. Это дает самый заметный прирост производительности без изменения кода конфигурации.