Как ускорить выполнение запроса к виртуальной таблице ДвиженияССубконто в 1С

Программист 1С v8.3 (Управляемые формы) Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При работе с крупными информационными базами запросы к регистрам бухгалтерии, особенно к виртуальной таблице ДвиженияССубконто, могут выполняться недопустимо долго. В рассматриваемой ситуации исходный запрос выполнялся около 5 минут для получения всего 4500 строк. Разберем пошагово, какие ошибки были допущены и как оптимизировать такой код, чтобы сократить время выполнения в десятки раз.

Анализ исходной проблемы

Проанализируем исходный запрос. Основная нагрузка ложится на фильтрацию по условию В, внутри которого выполняется вложенный запрос к остаткам. Кроме того, использование конструкции Регистратор.Дата и В ИЕРАРХИИ создает дополнительную нагрузку на СУБД. Выясним основные причины деградации производительности:

  1. Вложенные запросы в условиях: СУБД крайне тяжело оптимизировать конструкцию ГДЕ Поле В (ВЫБРАТЬ ...), если вложенная выборка велика.
  2. Обращение через точку: Поле Регистратор имеет составной тип. Обращение Регистратор.Дата заставляет систему делать LEFT JOIN со всеми таблицами документов, которые могут быть регистраторами.
  3. Неэффективные параметры виртуальных таблиц: Вычисления прямо в теле запроса (например, ДОБАВИТЬКДАТЕ) могут мешать использованию индексов.
  4. Двойное выполнение: Использование метода Пустой() перед выгрузкой фактически заставляет систему выполнять запрос дважды.

Решение 1: Использование временных таблиц и индексация

Первым делом рассмотрим самый эффективный метод — вынос предварительных расчетов во временную таблицу. Это позволяет SQL-серверу заранее подготовить набор данных и использовать его максимально быстро. Посмотрим на оптимизированный код:


// Сначала выбираем остатки номенклатуры во временную таблицу
Запрос.Текст = "ВЫБРАТЬ
|   ХозрасчетныйОстатки.Субконто1 КАК Номенклатура
|ПОМЕСТИТЬ ВТ_НоменклатураСИтогом
|ИЗ
|   РегистрБухгалтерии.Хозрасчетный.Остатки(&Дата, Счет В ИЕРАРХИИ (&СчетДТ), , Организация = &Организация) КАК ХозрасчетныйОстатки
|ИНДЕКСИРОВАТЬ ПО
|   Номенклатура;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|   ХозрасчетныйДвиженияССубконто.Регистратор КАК Регистратор,
|   МАКСИМУМ(ХозрасчетныйДвиженияССубконто.Период) КАК РегистраторДата
|ИЗ
|   РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(
|       &НачалоПериода,
|       &Дата,
|       СчетДт В ИЕРАРХИИ (&СчетДТ)
|           И Организация = &Организация
|           И СубконтоДт1 В (ВЫБРАТЬ Ном.Номенклатура ИЗ ВТ_НоменклатураСИтогом КАК Ном),
|       ,
|       ) КАК ХозрасчетныйДвиженияССубконто
|СГРУППИРОВАТЬ ПО
|   ХозрасчетныйДвиженияССубконто.Регистратор";

Обратите внимание на ключевой момент: мы добавили ИНДЕКСИРОВАТЬ ПО Номенклатура. Без индекса поиск по временной таблице в основном запросе будет осуществляться полным сканированием, что нивелирует пользу от её создания.

Решение 2: Замена Регистратор.Дата на Период

Рассмотрим ситуацию с обращением к полям через точку. В регистре бухгалтерии поле Период в 99% случаев совпадает с датой регистратора (если не используется специфическая настройка "Дата документа отличается от периода регистрации"). Поле Период является системным и проиндексированным в физической таблице.

Важный совет: Всегда заменяйте Регистратор.Дата на поле Период в запросах к регистрам. Это исключает необходимость соединения с таблицами документов и колоссально ускоряет работу.

Решение 3: Оптимизация передачи параметров

Проанализируем способ передачи дат. В исходном варианте использовалась функция ДОБАВИТЬКДАТЕ(&Дата, МЕСЯЦ, -12) прямо в параметрах виртуальной таблицы. Рассмотрим более правильный подход с точки зрения 1С: вычисление всех дат в коде и передача готовых значений.


НачалоПериода = ДобавитьМесяц(ТекущаяДатаСеанса(), -12);
НачалоПериода = НачалоДня(НачалоПериода); // Хорошая практика - приводить к началу дня

Запрос.УстановитьПараметр("Дата", ТекущаяДатаСеанса());
Запрос.УстановитьПараметр("НачалоПериода", НачалоПериода);

Это гарантирует, что SQL-сервер получит константу, а не выражение, которое может интерпретироваться по-разному в зависимости от плана запроса.

Решение 4: Исправление алгоритма выгрузки данных

Выясним причину лишней нагрузки в коде 1С. В исходном сообщении была ошибка:


Если Не Запрос.Выполнить().Пустой() Тогда
    Таблица = Запрос.Выполнить().Выгрузить();
КонецЕсли;

В данном фрагменте метод Выполнить() вызывается дважды. Каждый вызов — это полноценное обращение к СУБД. Для тяжелых запросов это означает, что пользователь ждет 5 минут для проверки на "пустоту" и еще 5 минут для получения данных. Перепишем этот фрагмент правильно:


РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
    Таблица = РезультатЗапроса.Выгрузить();
КонецЕсли;

Рекомендации по работе с иерархией

Проанализируем использование оператора В ИЕРАРХИИ. Если в параметре &СчетДТ передается группа счетов (например, весь 41 счет), платформа вынуждена подбирать все подчиненные счета. Если список счетов заранее известен, лучше программно сформировать массив ссылок на счета и использовать обычное условие В (&МассивСчетов). Это позволит индексу по полю Счет работать максимально эффективно.

Дополнительные методы ускорения

Если запрос все еще работает медленно, можно рассмотреть следующие варианты:

  1. Внутреннее соединение вместо условия В: Попробуйте выполнить ВНУТРЕННЕЕ СОЕДИНЕНИЕ основной таблицы с временной таблицей остатков вместо фильтрации в параметрах виртуальной таблицы. В некоторых случаях оптимизатор SQL лучше справляется с соединениями.
  2. Уточнение типов: Если вы знаете, что движения делают только документы РеализацияТоваровУслуг и ОтчетОРозничныхПродажах, добавьте условие ГДЕ Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг ИЛИ .... Это поможет сократить количество просматриваемых таблиц.
  3. Использование физических таблиц: Виртуальная таблица ДвиженияССубконто — самая сложная для системы. Если вам нужны данные только по первому субконто, иногда быстрее обратиться напрямую к основной таблице регистра (физической), где поля Субконто1, Субконто2 доступны напрямую.

Подводя итог, можно сказать, что перевод запроса на использование временных таблиц с индексацией и отказ от обращений через точку от Регистратора являются ключевыми факторами успеха. В данном примере это позволило сократить время выполнения запроса с 5 минут до 20 секунд.

← На главную