Как оптимизировать запрос с соединением виртуальной таблицы СрезПоследних?

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

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

Рассмотрим на конкретном примере, как исправить ошибку "Не следует использовать соединения с виртуальными таблицами" и написать эффективный и быстрый запрос.

Анализ исходной проблемы: почему запрос работает медленно?

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


ВЫБРАТЬ
    Контрагенты_Свойства.Руководитель КАК Руководитель,
    Контрагенты_Свойства.ГлавныйБухгалтер КАК ГлавныйБухгалтер,
    Контрагенты_ХарактеристикиСрезПоследних.ЛицоПодписывающееАктыРук КАК ЛицоПодписывающееАктыРук,
    Контрагенты_ХарактеристикиСрезПоследних.ЛицоПодписывающееАктыБух КАК ЛицоПодписывающееАктыБух
ИЗ
    Справочник.Контрагенты_Свойства КАК Контрагенты_Свойства
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.Контрагенты_Характеристики.СрезПоследних КАК Контрагенты_ХарактеристикиСрезПоследних
        ПО Контрагенты_Свойства.Владелец = Контрагенты_ХарактеристикиСрезПоследних.Контрагент
ГДЕ
    Контрагенты_Свойства.Владелец = &Контрагент
    И Контрагенты_Свойства.ПометкаУдаления = ЛОЖЬ

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

  1. Получение данных из виртуальной таблицы: Система обращается к РегистрСведений.Контрагенты_Характеристики.СрезПоследних. Поскольку параметры отбора для виртуальной таблицы не заданы, платформа 1С получает срез последних записей по ВСЕМ контрагентам, которые есть в регистре. Если их миллион — будет выбран миллион записей.
  2. Соединение таблиц: Полученная огромная таблица среза соединяется со справочником Контрагенты_Свойства.
  3. Фильтрация: И только после этого применяется условие ГДЕ Контрагенты_Свойства.Владелец = &Контрагент.

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

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

Чтобы оптимизировать запрос, необходимо изменить порядок действий: сначала выбрать минимально необходимые данные, а уже потом соединять их. Для этого идеально подходят временные таблицы (ВТ). Алгоритм будет состоять из двух шагов.

Шаг 1. Получаем данные из виртуальной таблицы с максимальным отбором

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

Результат этого маленького и быстрого запроса мы помещаем во временную таблицу.


ВЫБРАТЬ
    Контрагенты_ХарактеристикиСрезПоследних.ЛицоПодписывающееАктыРук КАК ЛицоПодписывающееАктыРук,
    Контрагенты_ХарактеристикиСрезПоследних.ЛицоПодписывающееАктыБух КАК ЛицоПодписывающееАктыБух,
    Контрагенты_ХарактеристикиСрезПоследних.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_ПодписывающиеЛица
ИЗ
    РегистрСведений.Контрагенты_Характеристики.СрезПоследних(&Дата, Контрагент = &Контрагент) КАК Контрагенты_ХарактеристикиСрезПоследних

Что здесь важно:

Шаг 2. Соединяем основную таблицу с подготовленной временной таблицей

Теперь, когда у нас есть небольшая временная таблица ВТ_ПодписывающиеЛица, содержащая только нужные данные, мы можем безопасно соединять ее с основной таблицей справочника.


ВЫБРАТЬ
    ВТ_ПодписывающиеЛица.ЛицоПодписывающееАктыРук,
    ВТ_ПодписывающиеЛица.ЛицоПодписывающееАктыБух,
    Контрагенты_Свойства.ГлавныйБухгалтер,
    Контрагенты_Свойства.Руководитель
ИЗ
    Справочник.Контрагенты_Свойства КАК Контрагенты_Свойства
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПодписывающиеЛица КАК ВТ_ПодписывающиеЛица
        ПО Контрагенты_Свойства.Владелец = ВТ_ПодписывающиеЛица.Контрагент
ГДЕ
    Контрагенты_Свойства.Владелец = &Контрагент

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

Итоговый оптимизированный запрос и дополнительные улучшения

Объединим оба шага в один пакетный запрос. Также добавим еще одно важное улучшение — индексацию временной таблицы.

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


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

Бонус: использование функции ПРЕДСТАВЛЕНИЕ()

Обратите внимание на строку ПРЕДСТАВЛЕНИЕ(Контрагенты_Свойства.Руководитель) КАК РуководительПредставление. Поля Руководитель и ГлавныйБухгалтер имеют ссылочный тип (например, Справочник.ФизическиеЛица). Если после выполнения запроса в коде вы обратитесь к свойству Выборка.Руководитель.Наименование, то платформа выполнит еще один неявный запрос к базе данных, чтобы получить наименование этого физического лица.

Чтобы избежать этих скрытых запросов и получить все данные за одно обращение к БД, используйте функцию ПРЕДСТАВЛЕНИЕ(). Она сразу вернет текстовое представление объекта (например, "Иванов Иван Иванович"), что является гораздо более производительным подходом.

Подведем итог: правило простое — никогда не соединяйте физические таблицы напрямую с "широкими" виртуальными таблицами. Всегда предварительно отбирайте из виртуальной таблицы только строго необходимые данные, используя ее параметры, помещайте результат во временную таблицу, индексируйте ее и только потом соединяйте с другими таблицами. Этот подход является стандартом написания производительных запросов в 1С.

← На главную