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