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

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

При работе с запросами в 1С:Предприятие 8 программисты часто сталкиваются с задачами выбора данных из нескольких табличных частей одного или разных документов. На первый взгляд простой запрос с использованием объединения ОБЪЕДИНИТЬ ВСЕ может стать причиной серьезных задержек в работе системы, особенно если количество документов в базе исчисляется тысячами, а строки табличных частей — десятками тысяч. В этой статье мы подробно разберем, как оптимизировать типичный запрос к табличным частям Товары и Оборудование документа ПоступлениеТоваровУслуг, и выясним, какие приемы позволяют значительно ускорить выполнение кода.

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

Рассмотрим исходный запрос, который требует оптимизации:


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

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

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

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

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

Рассмотрим этот алгоритм по шагам:

Шаг 1. Сбор сырых данных во временную таблицу. На этом этапе мы объединяем данные из разных табличных частей, но не обращаемся к свойствам через точку.


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

ОБЪЕДИНИТЬ ВСЕ

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

Шаг 2. Финальная агрегация и получение кодов. Теперь мы работаем с уже отобранным набором данных в tempdb. Группируем данные и только на выходе получаем Код страны происхождения.


ВЫБРАТЬ
    ВТ.СчетФактура,
    ВТ.СтранаПроисхождения.Код КАК КодСтраны,
    ВТ.НомерГТД,
    СУММА(ВТ.Количество) КАК КоличествоЭлементов
ИЗ
    ВТ_Данные КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.СчетФактура,
    ВТ.СтранаПроисхождения,
    ВТ.НомерГТД,
    ВТ.СтранаПроисхождения.Код

Обратите внимание: мы по-прежнему используем ВТ.СтранаПроисхождения.Код в финальном запросе, но теперь количество строк для соединения значительно меньше, так как мы работаем только с конкретными документами из списка.

Решение 2: Оптимизация работы с параметром списка документов

Проанализируем работу конструкции ТаблицаТоваров.Ссылка В (&СписокСФ). В платформе 1С реализован механизм, который по-разному транслирует это условие в SQL-запрос в зависимости от количества элементов в списке:

Чтобы план запроса всегда был стабильным и оптимальным, рекомендуется самостоятельно помещать список документов во временную таблицу через МенеджерВременныхТаблиц. Это избавит SQL-сервер от необходимости перестраивать план при изменении размера списка. Выполним это следующим образом:


// В коде 1С перед выполнением запроса:
Запрос.Текст = "ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТ_СписокСФ ИЗ &СписокСФ КАК Список";
// Далее в основном запросе используем:
// ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_СписокСФ КАК ВТ_Список ПО ТаблицаТоваров.Ссылка = ВТ_Список.Ссылка

Нужно ли использовать индексацию во временных таблицах?

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

В нашем случае, если мы просто группируем данные внутри одной временной таблицы ВТ_Данные и не соединяем её в следующем пакете с другой таблицей, индексация будет вредна. Создание индекса — это дополнительная нагрузка на дисковую подсистему и процессор сервера. Рассмотрим пример, где индекс был бы полезен:


// ... код создания ВТ_Данные ...
;
ИНДЕКСИРОВАТЬ ПО
    СтранаПроисхождения
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.СчетФактура,
    СпрСтраны.Код,
    ВТ.НомерГТД
ИЗ
    ВТ_Данные КАК ВТ
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СтраныМира КАК СпрСтраны
    ПО ВТ.СтранаПроисхождения = СпрСтраны.Ссылка

Здесь индекс по полю СтранаПроисхождения поможет SQL-серверу быстрее выполнить соединение с таблицей справочника.

Решение 3: Переход к использованию регистров

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

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

Итоги и рекомендации

Подводя итог, выделим основные правила оптимизации подобных запросов:

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

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

← На главную