При разработке сложных отчетов и обработок на платформе 1С программисты часто сталкиваются с вопросом производительности запросов, особенно когда речь идет о получении остатков и оборотов. Рассмотрим типичную ситуацию: у нас есть временная таблица с набором данных (например, номенклатура и склады), и нам нужно присоединить к ней остатки из регистра накопления. Возникает дилемма: достаточно ли указать условия связи в секции ПО (JOIN) или же необходимо дублировать эти же условия непосредственно в параметрах виртуальной таблицы?
В этой статье мы подробно разберем, как работает механизм виртуальных таблиц «под капотом», проанализируем влияние отборов на скорость выполнения запроса и выясним, какие приемы позволяют ускорить получение данных в десятки раз.
Для начала выясним причину, по которой дублирование условий является критически важным. Когда мы обращаемся к виртуальной таблице, такой как Остатки или Обороты, платформа 1С не просто считывает данные из одной физической таблицы. Вместо этого формируется вложенный SQL-запрос, который рассчитывает итоги на лету или выбирает их из таблиц итогов.
Рассмотрим два сценария:
ЛЕВОЕ СОЕДИНЕНИЕ ... ПО .... Это приводит к колоссальным затратам памяти и времени.Проанализируем практический пример: в некоторых случаях отсутствие условий в параметрах замедляет выполнение запроса в 60 и более раз. Это происходит потому, что вместо точечного поиска по индексу сервер выполняет полное сканирование (Table Scan или Index Scan) таблиц с миллионами записей.
Часто требуется связать данные по нескольким измерениям одновременно, например, по номенклатуре и заказу. Рассмотрим распространенную ошибку, которую совершают разработчики при попытке оптимизации.
Плохое решение (разрозненные условия):
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.УчетРезервовТМЦ.Остатки(
&КонецПериода,
Номенклатура В (ВЫБРАТЬ вт_Данные.Номенклатура ИЗ вт_Данные)
И Заказ В (ВЫБРАТЬ вт_Данные.Заказ ИЗ вт_Данные)
) КАК Остатки
В данном случае система выберет все остатки по всем товарам из вашей таблицы и все остатки по всем заказам. В результат попадут «лишние» данные: остатки товара А по заказу Б, даже если в вашей исходной временной таблице была только пара Товар А + Заказ А. Это не только замедляет запрос, но и может привести к логическим ошибкам при расчетах.
Правильное решение (кортежное сравнение):
Для максимально точной фильтрации следует использовать сравнение списков значений (кортежей). Разберем, как это выглядит в коде:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.УчетРезервовТМЦ.Остатки(
&КонецПериода,
(Номенклатура, Заказ) В (ВЫБРАТЬ вт_Данные.Номенклатура, вт_Данные.Заказ ИЗ вт_Данные)
) КАК Остатки
Этот синтаксис заставляет SQL-сервер использовать составные индексы регистра наиболее эффективным образом, выбирая только строго определенные комбинации измерений.
Выясним еще один важный аспект: если мы используем временную таблицу внутри параметров виртуальной таблицы (через В (ВЫБРАТЬ ...)), обязательна ли индексация? Ответ: однозначно да.
Посмотрим на ситуацию со стороны SQL-сервера. Чтобы выполнить условие IN (SELECT...), серверу нужно сопоставить данные. Если поле во временной таблице не проиндексировано, серверу придется сканировать временную таблицу столько раз, сколько записей он проверяет в основной таблице. Индексация полей связи во временной таблице позволяет использовать Nested Loops или более быстрые алгоритмы соединения.
Пример правильной подготовки временной таблицы:
ВЫБРАТЬ
Заказ,
Номенклатура,
Склад
ПОМЕСТИТЬ вт_Фильтр
ИЗ
Документ.ЗаказНаПроизводство.Товары КАК ЗаказТовары
ГДЕ
Ссылка = &Ссылка
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
Заказ,
Склад
;
Существует распространенный совет: «Никогда не соединяйте виртуальные таблицы напрямую, сначала выгрузите их во временные». Однако на практике это не всегда дает прирост скорости. Рассмотрим причины:
tempdb. Если данных мало (десятки или сотни строк), время на создание таблицы и запись в неё может превысить время самого расчета остатков.Поэтому выносить данные из виртуальной таблицы в отдельную временную таблицу стоит тогда, когда вы планируете использовать эти данные несколько раз в разных частях общего запроса, или если вы точно знаете, что SQL-оптимизатор строит неверный план из-за сложности запроса.
Проанализируем еще одну особенность производительности. Иногда передача списка номенклатуры через массив (как параметр запроса) работает быстрее, чем использование В (ВЫБРАТЬ Номенклатура ИЗ вт_Номенклатура).
IN (@P1, @P2, @P3...). Это эффективно для небольших списков (до 100-500 элементов).Подведем итог нашему исследованию и сформулируем правила эффективного использования виртуальных таблиц:
Остатки(...) или Обороты(...).(Измерение1, Измерение2) В (...) для фильтрации по нескольким полям. Это предотвращает выборку лишних данных.В (ВЫБРАТЬ...).Соблюдение этих правил позволит вам писать запросы, которые работают стабильно быстро даже при значительном росте объема данных в информационной базе.