Почему тормозят запросы с виртуальными таблицами в 1С и как это исправить?

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

Одной из наиболее распространенных проблем производительности в системе 1С:Предприятие 8.3 является медленная работа запросов, в которых используются виртуальные таблицы (например, Остатки, Обороты или СрезПоследних). Часто программисты сталкиваются с тем, что отчет, который быстро работал на тестовой базе, начинает «зависать» на реальных данных. В этой статье мы подробно разберем, почему это происходит, проанализируем внутренние механизмы работы СУБД и научимся правильно оптимизировать такие запросы.

Выясним причину: почему соединение виртуальных таблиц — это плохо?

Разберем ситуацию, когда в одном запросе соединяются две виртуальные таблицы, например, остатки товаров и цены из регистра сведений. На уровне СУБД виртуальная таблица не существует в виде физической таблицы. Это сложный вложенный запрос, который система 1С генерирует «на лету» при обращении к объекту. Если мы соединяем две такие таблицы напрямую через ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ, оптимизатор SQL-сервера часто не может построить эффективный план выполнения. Это приводит к так называемому «вложенному циклу» (Nested Loops) или полному сканированию таблиц, что катастрофически замедляет выполнение при росте объема данных.

Шаг 1: Правильное использование параметров виртуальных таблиц

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


// Неправильно: фильтрация после получения всех данных
ВЫБРАТЬ
    Остатки.Номенклатура,
    Остатки.КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Период) КАК Остатки
ГДЕ
    Остатки.Склад = &Склад

В данном случае система сначала получит остатки по всем складам из базы данных, и только потом отсечет ненужные. Чтобы ускорить работу, мы должны использовать параметры виртуальной таблицы. Рассмотрим правильный вариант:


// Правильно: использование параметров виртуальной таблицы
ВЫБРАТЬ
    Остатки.Номенклатура,
    Остатки.КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад) КАК Остатки

В этом случае в СУБД уйдет запрос, который сразу выберет данные только по конкретному складу, что значительно снизит нагрузку на дисковую подсистему и оперативную память.

Шаг 2: Использование временных таблиц для декомпозиции запроса

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

Проанализируем преимущества этого подхода:

  1. Упрощение плана запроса: СУБД проще работать с плоскими физическими таблицами (которыми становятся временные таблицы), чем с вложенными подзапросами.
  2. Возможность индексации: Мы можем явно указать поля, по которым нужно построить индекс во временной таблице, что критически важно для последующих соединений.
  3. Статистика: SQL-сервер получает точную статистику о количестве строк во временной таблице, что позволяет ему выбрать оптимальный метод соединения.

Посмотрим на пример реализации такого подхода в коде 1С:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|   ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
|   ТоварыНаСкладахОстатки.КоличествоОстаток КАК Количество
|ПОМЕСТИТЬ ВТ_Остатки
|ИЗ
|   РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Номенклатура В (&СписокНоменклатуры)) КАК ТоварыНаСкладахОстатки
|ИНДЕКСИРОВАТЬ ПО
|   Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|   ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
|   ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
|ПОМЕСТИТЬ ВТ_Цены
|ИЗ
|   РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, Номенклатура В (ВЫБРАТЬ ВТ_Остатки.Номенклатура ИЗ ВТ_Остатки)) КАК ЦеныНоменклатурыСрезПоследних
|ИНДЕКСИРОВАТЬ ПО
|   Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|   ВТ_Остатки.Номенклатура,
|   ВТ_Остатки.Количество,
|   ЕСТЬNULL(ВТ_Цены.Цена, 0) КАК Цена,
|   ВТ_Остатки.Количество * ЕСТЬNULL(ВТ_Цены.Цена, 0) КАК Сумма
|ИЗ
|   ВТ_Остатки КАК ВТ_Остатки
|       ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цены КАК ВТ_Цены
|       ПО ВТ_Остатки.Номенклатура = ВТ_Цены.Номенклатура";

РезультатЗапроса = Запрос.Выполнить();

Шаг 3: Важность индексации временных таблиц

Обратите внимание на ключевое слово ИНДЕКСИРОВАТЬ ПО в примере выше. Без индексации соединение даже временных таблиц может быть медленным, если в них содержится несколько тысяч строк. Мы рекомендуем всегда индексировать поля, по которым происходит связь (ПО) или условие (ГДЕ) в последующих частях запроса. В 1С 8.3 индексирование временной таблицы создает реальный индекс в базе данных tempdb SQL-сервера.

Шаг 4: Особенности работы со «СрезомПоследних»

Виртуальная таблица СрезПоследних является одной из самых «тяжелых». При ее вызове 1С генерирует запрос с использованием подзапроса для поиска максимальной даты на каждую комбинацию измерений. Если в регистре много данных и мало измерений, это может привести к серьезным задержкам. Рассмотрим профессиональный совет: если вам нужно получить цены для большого списка товаров, всегда сначала ограничивайте выборку в СрезПоследних с помощью условия в параметрах, как мы сделали в примере выше (Номенклатура В (ВЫБРАТЬ ВТ_Остатки.Номенклатура ИЗ ВТ_Остатки)).

Проанализируем ситуацию: использование «Выбрать Разрешенные»

При работе в базах с настроенным RLS (ограничение доступа на уровне записей) использование слова РАЗРЕШЕННЫЕ добавляет к вашему запросу множество дополнительных условий, которые накладываются на каждую таблицу. Если в запросе много соединений, итоговый текст SQL-запроса может вырасти в разы. В таких случаях декомпозиция через временные таблицы становится не просто рекомендацией, а обязательным условием. Разделив запрос, мы позволяем системе RLS отработать на маленьких выборках, что существенно повышает общую стабильность системы.

Подведем итоги

Для эффективной работы с данными в 1С мы должны придерживаться следующих правил:

Соблюдение этих простых, но мощных техник позволит вашим отчетам и обработкам работать максимально быстро даже на очень больших объемах данных, обеспечивая комфортную работу пользователей в системе 1С.

← На главную