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

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

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

В чем заключается проблема производительности?

Когда мы используем виртуальную таблицу РегистрСведений.Цены.СрезПоследних(&Период, ...) непосредственно в секции ЛЕВОЕ СОЕДИНЕНИЕ основного запроса, платформа 1С генерирует сложный SQL-запрос. СУБД (например, MS SQL Server или PostgreSQL) вынуждена выполнять расчет среза последних для всех записей регистра, прежде чем выполнить соединение с основной таблицей. Если в регистре миллионы записей, а нам нужно получить цены всего для десяти товаров, запрос будет выполняться неоправданно долго. Выясним причину: планировщик запросов не всегда может эффективно пробросить условия из основной таблицы внутрь виртуальной, что приводит к избыточному сканированию индексов.

Шаг 1: Анализ типичного неоптимального кода

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


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

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

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

Наиболее эффективным методом решения данной проблемы является использование объекта МенеджерВременныхТаблиц. Разберем алгоритм правильной работы:

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

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


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

Шаг 3: Передача параметров в виртуальную таблицу

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

Проанализируем ситуацию: при использовании конструкции Номенклатура В (ВЫБРАТЬ Ссылка ИЗ ВТ_СписокНоменклатуры) внутри параметров виртуальной таблицы, СУБД обрабатывает только те записи регистра, которые соответствуют списку. Это сокращает объем обрабатываемых данных с миллионов до единиц или сотен строк.

Шаг 4: Соединение результатов

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


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

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

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

  1. Минимизируйте количество соединений с виртуальными таблицами напрямую. Старайтесь сначала подготовить данные во временных таблицах.
  2. Всегда индексируйте поля, по которым происходит соединение во временных таблицах. Без индекса соединение больших объемов данных (более 1000 строк) может работать крайне медленно.
  3. Используйте типизацию. Если вы используете временную таблицу, убедитесь, что поля в ней имеют четко определенный тип, это помогает оптимизатору СУБД строить правильные планы запросов.
  4. Опасайтесь вложенных запросов. Вложенные запросы внутри параметров виртуальных таблиц (как в примере выше) работают хорошо, если во временной таблице немного записей. Если же записей очень много, иногда эффективнее использовать метод «передачи через параметры запроса» в коде 1С, предварительно выгрузив список ссылок в массив.

Когда стоит использовать СрезПоследнихОбъектов?

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

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

← На главную