Каталог решений - Оптимизация штатных запросов 7.7

Оптимизация штатных запросов 7.7

Оптимизация штатных запросов 7.7

В наличии

1С 7.7 выполняет запросы просто и бездумно — все что сказали, все и сделает. Никаких попыток оптимизации. Если в запросе есть переменная, например
"СвойствоПоставщика=Регистр.ПартииНаличие.Партия.Поставщик.ОсновноеСвойство;"
произойдет обращение к справочникам контрагентов и значений свойств, даже если переменная СвойствоПоставщика не используется ни в условиях, ни в группировках. Что оборачивается потерей быстродействия.

Категория:

Описание

1С 7.7 выполняет запросы просто и бездумно — все что сказали, все и сделает. Никаких попыток оптимизации. Если в запросе есть переменная, например
«СвойствоПоставщика=Регистр.ПартииНаличие.Партия.Поставщик.ОсновноеСвойство;»
произойдет обращение к справочникам контрагентов и значений свойств, даже если переменная СвойствоПоставщика не используется ни в условиях, ни в группировках. Что оборачивается потерей быстродействия.

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

Кардинальным методом решения проблемы могут явиться, например, прямые запросы, но и с использованием штатных возможностей можно ускорить выполнение запросов.

Идея следующая — если в тексте запроса не будет лишних переменных, не участвующих ни в условиях, ни в группировках, запрос зачастую не будет порождать дополнительных обращений к таблицам БД и ускорится.

Простенькая реализация «оптимизации» текста запроса встречается и в типовых конфигурациях. Например, в ТиС это выглядит примерно так (отчет «ОтчетПоПроектам»):

    ЕстьЗначение = 0;
    Если (ВыводитьЗаявки = 1) или (ВыводитьЗаказы = 1) Тогда
        ТекстЗапроса = ТекстЗапроса + «
            |Валюта = «;
    КонецЕсли;
    Если ВыводитьЗаявки = 1 Тогда
        ТекстЗапроса = ТекстЗапроса + «Регистр.Заявки.ДоговорПокупателя.ВалютаВзаиморасчетов»;
        ЕстьЗначение = 1;
    КонецЕсли;

Если валюта нам в запросе не нужна, переменная не добавляется и не будет лишнего обращения к реквизитам договора.

Метод действенный, но, к сожалению, не универсальный и неудобный, требующий больших переделок всех отчетов. Более удобно анализировать текст запроса перед выполнением запроса, когда уже есть все условия и группировки, и выкидывать из него ненужные строки.

Примерно так:

В каждый отчет вставляем перед выполнением запроса следующую строчку:

глПередВыполнениемЗапроса(Контекст, Запрос, ТекстЗапроса,»»);

А в глобальный модуль добавляется функция:

Процедура глПередВыполнениемЗапроса(Конт, Запрос, ТекстЗапроса, стрСписокФиксированныхПеременных=«») Экспорт
    Перем КоличествоСтрок, НомерСтроки, Позиция, ИмяПеременной;
    Перем СписокСтрокЗапроса, СписокУдаляемыхПеременных, СписокФиксированныхПеременных;
    Перем Строчка;
    Если стрСписокФиксированныхПеременных<>«*» Тогда
        // Если Константа.ЗапретитьОптимизациюТекстаЗапроса<>1 Тогда
         СписокФиксированныхПеременных = глРазделитьВСписок(стрСписокФиксированныхПеременных);
            //
            // Убираем комментарии из текста запроса
            //
            КоличествоСтрок = СтрКоличествоСтрок(ТекстЗапроса);
            времТекстЗапроса = «»;
            Для НомерСтроки = 1 По КоличествоСтрок Цикл
                Строчка = СтрПолучитьСтроку(ТекстЗапроса,НомерСтроки);
                Позиция = Найти(Строчка,«//»);
                Если Позиция<>0 Тогда
                    Строчка = Прав(Строчка,Позиция1);
                КонецЕсли;
                Если ПустаяСтрока(Строчка)=Тогда
                    времТекстЗапроса = времТекстЗапроса + Строчка + РазделительСтрок;
                КонецЕсли;
            КонецЦикла;
            ТекстЗапроса = времТекстЗапроса;
            //
            // Текст запроса преобразуется к такому виду, что бы одна строка запроса (до точки с запятой)
            // соответствовала одной строке в тексте.
            //
            ТекстЗапроса = СтрЗаменить(ТекстЗапроса,РазделительСтрок,» «);
            ТекстЗапроса = СтрЗаменить(ТекстЗапроса,«;»,«;»+РазделительСтрок);
            //
            // Разбиваем текст запроса на отдельные строки с выделением имен переменных
            //
            КоличествоСтрок = СтрКоличествоСтрок(ТекстЗапроса);
            СписокСтрокЗапроса = СоздатьОбъект(«СписокЗначений»);
            СписокУдаляемыхПеременных = СоздатьОбъект(«СписокЗначений»);
            Для НомерСтроки = 1 По КоличествоСтрок Цикл
                Строчка = СтрПолучитьСтроку(ТекстЗапроса,НомерСтроки);
                Позиция = Найти(Строчка,«=»);
                ИмяПеременной = «»;
                Если Позиция<>0 Тогда
                    ИмяПеременной = СокрЛП(Лев(Строчка,Позиция1));
                    Если (Найти(ИмяПеременной,» «)<>0) ИЛИ (Найти(ИмяПеременной,«(«)<>0) Тогда
                        ИмяПеременной = «»;
                    ИначеЕсли СписокФиксированныхПеременных.НайтиЗначение(ИмяПеременной)=Тогда
                        СписокУдаляемыхПеременных.ДобавитьЗначение(ИмяПеременной);
                    КонецЕсли;
                КонецЕсли;
                СписокСтрокЗапроса.ДобавитьЗначение(ИмяПеременной,Строчка);
            КонецЦикла;
            //
            // Формируем список удаляемых переменных, то есть тех, которые не участвуют в функциях и
            // группировках запроса
            //
            КоличествоСтрок = СписокСтрокЗапроса.РазмерСписка();
            Для НомерСтроки = 1 По КоличествоСтрок Цикл
                ИмяПеременной = СписокСтрокЗапроса.ПолучитьЗначение(НомерСтроки, Строчка);
                Если ПустоеЗначение(ИмяПеременной)=Тогда
                    Позиция = 1;
                    Пока Позиция <= СписокУдаляемыхПеременных.РазмерСписка() Цикл
                        Если Найти(ВРег(Строчка),ВРег(СписокУдаляемыхПеременных.ПолучитьЗначение(Позиция)))<>Тогда
                            СписокУдаляемыхПеременных.УдалитьЗначение(Позиция);
                        ИначеЕсли (СписокУдаляемыхПеременных.ПолучитьЗначение(Позиция)=«ТекущийДокумент») И (Найти(ВРег(Строчка),«ГРУППИРОВКА»)<>0) И (Найти(ВРег(Строчка),» ДОКУМЕНТ»)<>0) Тогда
                            СписокУдаляемыхПеременных.УдалитьЗначение(Позиция);
                        Иначе
                            Позиция = Позиция + 1;
                        КонецЕсли;
                    КонецЦикла;
                    Если СписокУдаляемыхПеременных.РазмерСписка()=Тогда
                        Прервать;
                    КонецЕсли;
                КонецЕсли;
            КонецЦикла;
            //
            // Формируем текст запроса с выкидыванием неиспользуемых переменных
            //
            ТекстЗапроса = «»;
            КоличествоСтрок = СписокСтрокЗапроса.РазмерСписка();
            Для НомерСтроки = 1 По КоличествоСтрок Цикл
                ИмяПеременной = СписокСтрокЗапроса.ПолучитьЗначение(НомерСтроки, Строчка);
                Если ПустоеЗначение(ИмяПеременной)=Тогда
                    ТекстЗапроса = ТекстЗапроса + Строчка + РазделительСтрок;
                ИначеЕсли СписокУдаляемыхПеременных.НайтиЗначение(ИмяПеременной)=Тогда
                    ТекстЗапроса = ТекстЗапроса + Строчка + РазделительСтрок;
                КонецЕсли;
            КонецЦикла;
        // КонецЕсли
    КонецЕсли;
КонецПроцедуры // глПередВыполнениемЗапроса

В первое время желательно еще добавить константу «ЗапретитьОптимизациюТекстаЗапроса» и раскомментарить условие с ней. Это позволит, когда вылезет ошибка в формировании какого-нибудь отчета, временно запретить оптимизацию и неспешно разбираться, какую же переменную запроса нужно включить в исключения.

Результат:

Выполнение запросов ускоряется, ускорение зависит от реально используемых в запросе переменных и группировок, наибольшее ускорение достигается, если отчет формируется без подробностей, сводно.

По факту, в моей базе, формирование ведомости по партиям с групировками Фирма/Поставщик/Склад ускорилось в три раза во сравнению с неоптимизированным запросом. При использовании группировок типа Партия или Документ эффект от оптимизации почти пропадает. В общем, все зависит от переменных, которых вы надобавляли в свой запрос. Если у вас много группировок вида «СвойствоПоставщика=Регистр.ПартииНаличие.Партия.Поставщик.ОсновноеСвойство», эффект от оптимизации может быть огромным.

P.S.

В некоторых отчетах используются дополнительные переменные запроса, не участвующие ни в условиях, ни в группировках. Такие переменные необходимо включить в исключения, что бы оптимизация не удалила их из текста запроса.

Например, в штатном отчете ТиС «Отчет по продажам ТМЦ» вызов процедуры оптимизации текста запроса должен выглядеть так:

глПередВыполнениемЗапроса(Контекст, Запрос, ТекстЗапроса,«ДатаДокумента»);

P.P.S.

Дополнительные функции. глРазделить() обычно есть в типовых, глРазделитьВСписок() обычно нет или по другому называется.

//******************************************************************************
//    глРазделить(Стр,Разделитель)
//
//    Параметры:
//        Стр            — строка, которую необходимо разложить на подстроки
//        Разделитель — строка-разделитель, по умолчанию — запятая
//
//    Возвращаемое значение:
//        Многострочная строка, составленная из подстрок
//
//    Описание:
//        Разбивает строку на многострочную строку по разделителю
//
Функция глРазделить(Знач Стр,Разделитель=«,») Экспорт
    Если Разделитель=» « Тогда
        Пока Найти(Стр,»  «)>Цикл
            Стр = СтрЗаменить(Стр,»  «,» «)
        КонецЦикла
    КонецЕсли;
    Возврат СтрЗаменить(Стр,Разделитель,РазделительСтрок)
КонецФункции // глРазделить()

//******************************************************************************
//    глРазделитьВСписок(Стр,Разделитель)
//
//    Параметры:
//        Стр            — строка, которую необходимо разложить на подстроки
//        Разделитель — строка-разделитель, по умолчанию — запятая
//
//    Возвращаемое значение:
//        Список значений, составленный из подстрок
//
//    Описание:
//        Разбивает строку в список значений по разделителю
//
Функция глРазделитьВСписок(Знач Стр,Разделитель=«,»,ПризнакПометки=«») Экспорт
    Перем Текст, Номер, Список, Строчка;
    Текст = глРазделить(Стр,Разделитель);
    Список = СоздатьОбъект(«СписокЗначений»);
    Если ПустоеЗначение(Стр)=Тогда
        Для Номер=По СтрКоличествоСтрок(Текст) Цикл
            Строчка = СокрЛП(СтрПолучитьСтроку(Текст,Номер));
            Если ПустоеЗначение(Строчка)=Тогда
                Если ПустаяСтрока(ПризнакПометки)=Тогда
                    Список.ДобавитьЗначение(Строчка);
                ИначеЕсли Лев(Строчка,СтрДлина(ПризнакПометки)) <> ПризнакПометки Тогда
                    Список.ДобавитьЗначение(Строчка);
                Иначе
                    Список.ДобавитьЗначение(СокрЛП(Сред(Строчка,СтрДлина(ПризнакПометки)+1)));
                    Список.Пометка(Список.РазмерСписка(),1);
                КонецЕсли;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    Возврат Список;
КонецФункции // глРазделитьВСписок()

has been added to your cart:
Оформление заказа