Как оптимизировать порядок выполнения запросов к виртуальным таблицам и избежать проблем при обращении через точку к регистратору?

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

В процессе разработки на платформе «1С:Предприятие» перед программистом часто встает вопрос: как именно СУБД и платформа обрабатывают наши запросы? Понимание внутреннего механизма выполнения запроса — это ключ к созданию быстродействующих систем. Сегодня мы разберем одну из самых «болезненных» тем для производительности: использование регистраторов в виртуальных таблицах и коварство обращений через точку.

Порядок выполнения условий: Параметры виртуальной таблицы против ГДЕ

Начнем с фундаментального вопроса: что выполнится раньше — отбор в параметрах виртуальной таблицы или условие в секции ГДЕ? Рассмотрим эту ситуацию подробнее. Для платформы 1С существует принципиальная разница в обработке этих конструкций.

Когда мы указываем условия в параметрах виртуальной таблицы (например, РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, , )), платформа формирует запрос к СУБД таким образом, что фильтрация происходит максимально рано. СУБД получает команду выбрать данные, которые уже ограничены периодом или значениями измерений. Это позволяет значительно сократить объем данных, которые необходимо прочитать с диска и передать в память.

Если же мы переносим условия в секцию ГДЕ, ситуация меняется. Рассмотрим пример:


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

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

Ловушка регистратора и производительность виртуальных таблиц

Многие начинающие разработчики удивляются, почему запрос к виртуальной таблице Обороты начинает «тормозить», как только в списке выбранных полей появляется Регистратор. Разберем причину этого явления.

Данные в регистрах накопления хранятся в двух видах таблиц:

  1. Физическая таблица движений: хранит каждую запись (каждый документ), сделавший движение.
  2. Таблицы итогов: хранят свернутые данные (агрегаты) по периодам (обычно по месяцам) в разрезе измерений.

Виртуальная таблица Обороты — это мощный инструмент, который умеет мгновенно брать данные из таблиц итогов. Однако в таблицах итогов нет поля Регистратор, так как итоги свернуты. Как только вы добавляете поле Регистратор или устанавливаете периодичность Авто или Регистратор, платформа вынуждена игнорировать таблицы итогов. Она обращается напрямую к тяжелой физической таблице движений и выполняет группировку «на лету».

Посмотрим на пример запроса, который лишает виртуальную таблицу ее преимуществ:


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

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

Опасность неявных соединений (обращение «через точку»)

Рассмотрим ситуацию из обсуждения: обращение ВыручкаИСебестоимостьПродаж.Регистратор.Организация. На первый взгляд, это удобно. Но с точки зрения SQL — это катастрофа для производительности, если Регистратор имеет составной тип данных.

Каждое обращение через точку в 1С преобразуется в оператор LEFT JOIN. Если поле Регистратор может принимать значения 50 различных типов документов, то при написании Регистратор.Организация СУБД будет вынуждена выполнить 50 соединений со всеми возможными таблицами документов, чтобы найти, в какой из них лежит нужная организация. Это порождает огромный SQL-запрос, который создает колоссальную нагрузку на сервер.

Как мы можем оптимизировать такую ситуацию? Проанализируем варианты:

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

Порядок условий в предложении ГДЕ

Существует миф, что порядок условий в ГДЕ (например, сначала ссылка, потом дата) критически важен. Выясним правду. Современные СУБД (MS SQL Server, PostgreSQL) используют оптимизатор на основе стоимостей. Перед выполнением запроса СУБД анализирует статистику данных и сама решает, какое условие применить первым.

Например, в запросе к табличной части документа:


ГДЕ
    РеализацияТовары.Ссылка = &Ссылка
    И РеализацияТовары.Номенклатура.ТипНоменклатуры <> ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Услуга)

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

Практические рекомендации по работе с временными таблицами

Для сложных отборов мы рекомендуем использовать временные таблицы. Это помогает «подсказать» оптимизатору СУБД правильный путь выполнения. Рассмотрим по шагам:

  1. Поместите список ссылок (например, документов или номенклатуры) во временную таблицу.
  2. Проиндексируйте временную таблицу по ключевым полям.
  3. Соедините временную таблицу с основной выборкой.

Такой подход гарантирует, что СУБД не будет строить неэффективные планы выполнения при работе с большими объемами данных.

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

← На главную