Оптимизация запросов — это одна из самых важных и сложных задач при разработке на платформе 1С:Предприятие 8.3. Часто разработчики сталкиваются с ситуацией, когда запрос, работающий быстро на небольшом объеме данных, начинает существенно замедлять систему при росте информационной базы. В данной статье мы разберем основные методы оптимизации, использование временных таблиц и научимся правильно работать с индексами.
Проанализируем типичную ошибку: использование вложенных запросов в условии ГДЕ или в качестве источника данных для соединения. Когда мы используем вложенный запрос в секции СОЕДИНЕНИЕ, СУБД зачастую не может эффективно построить план выполнения. Для каждой строки основной таблицы системе приходится выполнять вложенный запрос заново или создавать временную структуру в оперативной памяти без индексов.
Рассмотрим пример неоптимального кода:
ВЫБРАТЬ
Продажи.Номенклатура,
Продажи.КоличествоОборот
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон) КАК Продажи
ГДЕ
Продажи.Номенклатура В
(ВЫБРАТЬ
Номенклатура
ИЗ
Справочник.Номенклатура
ГДЕ
ВидНоменклатуры = &ВидТовара)
Выясним причину проблемы: при выполнении такого запроса СУБД (например, MS SQL Server) может выполнить подзапрос для каждой записи из таблицы оборотов, что приведет к катастрофическому падению производительности. Чтобы избежать этого, мы разберем использование временных таблиц.
Временные таблицы позволяют разделить один сложный запрос на несколько простых этапов. Это значительно упрощает работу оптимизатору СУБД. Проанализируем, как переписать предыдущий пример правильно.
Разберем по шагам процесс создания временной таблицы:
ПОМЕСТИТЬ.Посмотрим на пример правильной реализации:
ВЫБРАТЬ
Номенклатура.Ссылка КАК НоменклатураСсылка
ПОМЕСТИТЬ ВТ_НужныеТовары
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ВидНоменклатуры = &ВидТовара
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Продажи.Номенклатура,
Продажи.КоличествоОборот
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон) КАК Продажи
ГДЕ
Продажи.Номенклатура В (ВЫБРАТЬ ВТ_НужныеТовары.НоменклатураСсылка ИЗ ВТ_НужныеТовары)
Обратите внимание на символ ; — он разделяет пакеты запросов. Это позволяет выполнить всю цепочку действий за один вызов сервера БД.
Создание временной таблицы — это только половина дела. Если временная таблица содержит более нескольких сотен строк, отсутствие индекса может свести на нет всю пользу от её использования. Индекс позволяет СУБД мгновенно находить нужные записи при соединении с другими таблицами.
Добавим индексирование в наш пример. Рассмотрим синтаксис инструкции ИНДЕКСИРОВАТЬ ПО:
ВЫБРАТЬ
Номенклатура.Ссылка КАК НоменклатураСсылка
ПОМЕСТИТЬ ВТ_НужныеТовары
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ВидНоменклатуры = &ВидТовара
ИНДЕКСИРОВАТЬ ПО
НоменклатураСсылка
;
Важный момент: индексировать стоит только те поля, которые участвуют в условиях ГДЕ или в секциях СОЕДИНЕНИЕ. Избыточное индексирование также может замедлить работу, так как системе требуется время на построение самого индекса.
Иногда возникает необходимость использовать одну и ту же временную таблицу в разных вызовах запросов из программного кода. Для этого в платформе 1С предусмотрен объект МенеджерВременныхТаблиц. Рассмотрим подробнее, как работать с ним в коде 1С.
Проанализируем ситуацию: нам нужно сначала отобрать список контрагентов-должников, а затем в разных процедурах анализировать их продажи и оплаты.
// Создаем менеджер временных таблиц
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Первый запрос: формируем список должников
Запрос.Текст =
"ВЫБРАТЬ
| Взаиморасчеты.Контрагент КАК Контрагент
|ПОМЕСТИТЬ ВТ_Должники
|ИЗ
| РегистрНакопления.Взаиморасчеты.Остатки КАК Взаиморасчеты
|ГДЕ
| Взаиморасчеты.СуммаОстаток > 0
|ИНДЕКСИРОВАТЬ ПО
| Контрагент";
Запрос.Выполнить();
// Теперь менеджер МенеджерВТ содержит таблицу ВТ_Должники
// Мы можем использовать ее в другом запросе, просто передав менеджер
НовыйЗапрос = Новый Запрос;
НовыйЗапрос.МенеджерВременныхТаблиц = МенеджерВТ;
НовыйЗапрос.Текст =
"ВЫБРАТЬ
| Продажи.Контрагент,
| Продажи.СуммаОборот
|ИЗ
| РегистрНакопления.Продажи.Обороты КАК Продажи
|ГДЕ
| Продажи.Контрагент В (ВЫБРАТЬ ВТ_Должники.Контрагент ИЗ ВТ_Должники)";
Результат = НовыйЗапрос.Выполнить().Выгрузить();
// Не забываем закрыть менеджер, чтобы освободить память в TempDB
МенеджерВТ.Закрыть();
Часто возникает вопрос: что быстрее — использовать ЛЕВОЕ СОЕДИНЕНИЕ с временной таблицей или условие В (ВЫБРАТЬ...)? Рассмотрим этот вопрос подробнее.
С точки зрения СУБД, если поля проиндексированы, разница в скорости будет минимальной. Однако ВНУТРЕННЕЕ СОЕДИНЕНИЕ часто работает эффективнее, так как оптимизатор СУБД лучше понимает зависимости между данными. Разберем пример с соединением:
ВЫБРАТЬ
Продажи.Номенклатура,
Продажи.КоличествоОборот
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон) КАК Продажи
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_НужныеТовары КАК НужныеТовары
ПО Продажи.Номенклатура = НужныеТовары.НоменклатураСсылка
Помните: при использовании ЛЕВОЕ СОЕДИНЕНИЕ с большими таблицами всегда старайтесь, чтобы правая таблица была максимально компактной и проиндексированной. Если вам нужно просто отфильтровать данные, ВНУТРЕННЕЕ СОЕДИНЕНИЕ — ваш лучший выбор.
Разберем основные ошибки, которые совершают программисты 1С при попытке оптимизации:
Закрыть() или удаляйте ненужные таблицы вручную через УНИЧТОЖИТЬ ВТ_ИмяТаблицы.Мы проанализировали основные инструменты оптимизации запросов в 1С 8.3. Использование временных таблиц в сочетании с МенеджерВременныхТаблиц и правильным индексированием — это «золотой стандарт» разработки высокопроизводительных систем. Применяя эти правила на практике, вы сможете ускорить формирование отчетов и проведение документов в несколько раз, обеспечивая комфортную работу пользователей.