При разработке отчетов в платформе 1С:Предприятие 7.7 разработчики часто сталкиваются с ситуацией, когда попытка получить реквизиты документа-регистратора (такие как дата и номер) непосредственно в тексте запроса приводит к драматическому падению скорости работы программы. Запрос, выполнявшийся за 1 секунду, при добавлении обращений к дате и номеру документа начинает выполняться 50 секунд и более.
Рассмотрим подробно причины такого поведения встроенного ("черного") движка запросов 1С 7.7 и проанализируем штатные способы решения этой задачи.
Проанализируем пример кода, в котором разворачивается группировка по регистратору с попыткой вытащить номер и дату документа через составное обращение:
ЗП = СоздатьОбъект("Запрос");
ТЗ = "Период с НачДата По КонДата;
|Склад = Регистр.Прибыль.Склад;
|Товар = Регистр.Прибыль.Товар;
|Количество = Регистр.Прибыль.Количество;
|ТекДок = Регистр.Прибыль.ТекущийДокумент;
|ТекДокНомерДок = Регистр.Прибыль.ТекущийДокумент.ЗакрытиеСмены.НомерДок,
| Регистр.Прибыль.ТекущийДокумент.НакладнаяРасход.НомерДок,
| Регистр.Прибыль.ТекущийДокумент.ОтчетРеализатора.НомерДок;
|ТекДокДатаДок = Регистр.Прибыль.ТекущийДокумент.ЗакрытиеСмены.ДатаДок,
| Регистр.Прибыль.ТекущийДокумент.НакладнаяРасход.ДатаДок,
| Регистр.Прибыль.ТекущийДокумент.ОтчетРеализатора.ДатаДок;
|Группировка ТекДок;
|Функция Продано = Сумма(Количество);";
Выясним причину замедления на уровне СУБД в клиент-серверном режиме (MS SQL Server):
ТекущийДокумент в регистре имеет составной тип («Документ неопределенного вида»), платформа не может извлечь номер и дату напрямую из одной таблицы.LEFT OUTER JOIN) к таблицам шапок (DH*) для каждого указанного вида документа.RA*) оптимизатор SQL Server прекращает эффективно использовать индексы, переходя к полному сканированию таблиц (Table Scan). В итоге время выполнения запроса возрастает в десятки раз.Самый простой и эффективный штатный способ — вынести получение даты и номера документа из самого текста запроса в цикл обработки выборки.
Посмотрим, как выглядит оптимизированный текст запроса:
ЗП = СоздатьОбъект("Запрос");
ТЗ = "Период с НачДата По КонДата;
|Склад = Регистр.Прибыль.Склад;
|Товар = Регистр.Прибыль.Товар;
|Количество = Регистр.Прибыль.Количество;
|ТекДок = Регистр.Прибыль.ТекущийДокумент;
|Группировка Склад Без Групп;
|Группировка Товар Без Групп;
|Группировка ТекДок;
|Функция Продано = Сумма(Количество);";
Если ЗП.Выполнить(ТЗ) = 1 Тогда
Пока ЗП.Группировка("Склад") = 1 Цикл
Пока ЗП.Группировка("Товар") = 1 Цикл
Пока ЗП.Группировка("ТекДок") = 1 Цикл
// Получаем дату и номер штатно через объект документа
ДокДата = ЗП.ТекДок.ДатаДок;
ДокНомер = ЗП.ТекДок.НомерДок;
// Выводим данные в таблицу или обрабатываем дальше
КонецЦикла;
КонецЦикла;
КонецЦикла;
КонецЕсли;
При таком подходе сам запрос выполняется за считанные секунды. При обходе группировки ТекДок платформа быстро выбирает точные данные по конкретному IDDOC из кэша или точечным SQL-запросом.
Если отчет по регистру строится часто и за большие периоды, рационально изменить структуру регистра в Конфигураторе:
Прибыль новые реквизиты: ДатаДокумента (тип "Дата") и НомерДокумента (тип "Строка").ДатаДок и НомерДок.
|ТекДокДата = Регистр.Прибыль.ДатаДокумента;
|ТекДокНомер = Регистр.Прибыль.НомерДокумента;
В этом случае данные читаются напрямую из таблицы движений регистра RA* без выполнения дополнительных операторов JOIN, что дает максимальную скорость выполнения.
Разберем еще одну штатную возможность платформы. Если у регистра включено свойство "Быстрая обработка движений" (или установлены галки отбора по измерениям), платформа оптимизирует выборку регистраторов. Тем не менее, использование прямых реквизитов или обработка в цикле остаются предпочтительными методами для достижения наибольшего быстродействия.
Резюме: Избегайте перечисления конструкций вида ТекущийДокумент.ВидДок.НомерДок через запятую в тексте встроенных запросов 1С 7.7. Для получения высокой скорости извлекайте в запросе сам объект ТекущийДокумент, а его реквизиты читайте при выгрузке или обходе результатов выборки.