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