При переходе с файловой базы или Microsoft SQL Server на СУБД PostgreSQL разработчики 1С часто сталкиваются с ситуацией, когда стандартные динамические списки начинают работать значительно медленнее. Сам список без фильтров может открываться мгновенно, однако при установке условий или отборов форма зависает на длительное время. Разберем по шагам, почему возникает эта проблема и как оптимизировать работу динамического списка на PostgreSQL.
Проанализируем механику работы динамического списка и СУБД PostgreSQL. Основные факторы, приводящие к резкому падению производительности:
РАЗНОСТЬДАТ(Контрагенты.ДатаРождения, &ТекущаяДата, ГОД) и на поле накладывается отбор компоновки данных, СУБД не может применить индекс. Чтобы вычислить возраст для каждой записи, PostgreSQL вынужден выполнять полное последовательное сканирование таблицы (Seq Scan), производя расчет функции над каждой строкой.ВЫБРАТЬ РАЗЛИЧНЫЕ. Ключевое слово DISTINCT блокирует постраничную выборку порциями (курсором). Платформа 1С в таком случае считывает всю выборку целиком и выполняет сортировку во временных файлах на диске.WHERE по первичному ключу, формируя сложные вложенные подзапросы.Ссылка, Код), дополнительные реквизиты объектов по умолчанию не индексируются.Выясним, как правильно организовать фильтрацию по датам. Вместо вычисления возраста «на лету» в выражении отбора компоновки данных необходимо пересчитывать граничное условие в коде 1С и передавать в запрос сравнение с реальным полем даты.
Рассмотрим пример кода на сервере, в котором мы рассчитываем пороговую дату и передаем ее в параметр запроса динамического списка:
&НаСервере
Процедура УстановитьОтборПоВозрастуНаСервере(ОтборВозрастДо)
// Рассчитываем пороговую дату в коде 1С
Если ОтборВозрастДо > 0 Тогда
ПороговаяДата = ДобавитьМесяц(ТекущаяДатаСеанса(), -ОтборВозрастДо * 12);
// Устанавливаем условие непосредственно на реквизит даты
Список.Параметры.УстановитьЗначениеПараметра("ТекДатаМинусГоды", ПороговаяДата);
Список.Параметры.УстановитьЗначениеПараметра("ПрименятьОтборПоВозрасту", Истина);
Иначе
Список.Параметры.УстановитьЗначениеПараметра("ТекДатаМинусГоды", Дата(1, 1, 1));
Список.Параметры.УстановитьЗначениеПараметра("ПрименятьОтборПоВозрасту", Ложь);
КонецЕсли;
КонецПроцедуры
В тексте запроса динамического списка условие фильтрации должно выглядеть следующим образом:
ВЫБРАТЬ
Контрагенты.Ссылка КАК Ссылка,
Контрагенты.Наименование КАК Наименование,
Контрагенты.ДатаРожденияКлиента КАК ДатаРожденияКлиента,
Контрагенты.Менеджер КАК Менеджер
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
НЕ Контрагенты.ЭтоГруппа
И (&ПрименятьОтборПоВозрасту = ЛОЖЬ ИЛИ Контрагенты.ДатаРожденияКлиента >= &ТекДатаМинусГоды)
Посмотрим на результат: PostgreSQL выполняет прямое сравнение значений даты с константой, что позволяет планировщику СУБД задействовать индекс по полю ДатаРожденияКлиента.
Разберем обязательные правила настройки динамических списков для обеспечения максимального быстродействия:
Справочник.Контрагенты). Это позволяет платформе оптимизировать построение условий и использовать выборку по первичному ключу.ВЫБРАТЬ РАЗЛИЧНЫЕ. Если в результате объединения таблиц появляются дубли, перепишите запрос так, чтобы избавиться от размножения строк без использования DISTINCT.ДатаРожденияКлиента и установите свойство Индексировать в значение Индексировать или Индексировать с доп. упорядочиванием.Если условий отбора много, можно формировать текст запроса динамически, чтобы не перегружать СУБД лишними проверками параметров через ИЛИ. Рассмотрим реализацию динамической подстановки условий:
&НаСервере
Процедура ОбновитьЗапросДинамическогоСписка()
БазовыйТекст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка,
| Контрагенты.Наименование КАК Наименование,
| Контрагенты.ДатаРожденияКлиента КАК ДатаРожденияКлиента
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| НЕ Контрагенты.ЭтоГруппа";
Если ЗначениеЗаполнено(ОтборВозрастДо) И ОтборВозрастДо > 0 Тогда
ПороговаяДата = ДобавитьМесяц(ТекущаяДатаСеанса(), -ОтборВозрастДо * 12);
БазовыйТекст = БазовыйТекст + " И Контрагенты.ДатаРожденияКлиента >= &ПороговаяДата";
Список.ТекстЗапроса = БазовыйТекст;
Список.Параметры.УстановитьЗначениеПараметра("ПороговаяДата", ПороговаяДата);
Иначе
Список.ТекстЗапроса = БазовыйТекст;
КонецЕсли;
КонецПроцедуры
Проанализируем состояние самой СУБД. При миграции базы данных или массовой загрузке данных планировщик PostgreSQL может не иметь актуальной статистики распределения значений в таблицах.
VACUUM ANALYZE;
Убедитесь, что параметр autovacuum в postgresql.conf включен (autovacuum = on).random_page_cost = 4.0 завышено, из-за чего планировщик игнорирует индексы. Установите в postgresql.conf:
random_page_cost = 1.1
from_collapse_limit = 20
join_collapse_limit = 20
Сочетание правильного построения запросов, индексации реквизитов и актуализации статистики PostgreSQL позволяет сократить время отклика динамического списка с нескольких минут до долей секунды.