В практике разработки на платформе 1С:Предприятие часто встречается догма: «Используй временные таблицы везде, где это возможно, чтобы облегчить работу оптимизатору СУБД». Однако случаются ситуации, когда запрос, переписанный на временные таблицы, начинает работать в разы медленнее своего «неправильного» аналога с подзапросами. В этой статье мы детально проанализируем такой кейс, выясним причины неожиданного поведения системы и найдем максимально эффективное решение для работы с большими объемами данных.
Рассмотрим ситуацию, с которой столкнулся автор темы. Имеются две версии одного и того же запроса, цель которых — собрать данные по электронным счетам-фактурам (ЭСФ), связанным с конкретным документом реализации через цепочку оснований. В таблице ЭСФ более миллиона записей.
Запрос №1 (с использованием временной таблицы):
ЭСФ во временную таблицу втЭСФ.втЭСФ по полю СчетФактура.Запрос №2 (с использованием подзапроса в условии ГДЕ ... В):
Вместо создания промежуточной таблицы, условие фильтрации ЭСФ помещено непосредственно внутрь основного запроса через конструкцию В (ВЫБРАТЬ...).
Результат замера: Запрос №2 работает в 3-4 раза быстрее. Разберемся, почему так происходит.
Когда мы выполняем команду ПОМЕСТИТЬ, СУБД (особенно если речь идет о файловой базе данных) вынуждена физически записать данные на диск во временный файл. Если в выборку по условию «не отозван» попадает почти вся таблица ЭСФ (более миллиона строк), процесс создания этой таблицы и её последующего индексирования занимает огромное количество времени.
Проанализируем ситуацию: стоит ли копировать миллион записей, чтобы в итоге выбрать из них всего 30-50 строк, относящихся к одной реализации? Очевидно, что нет. В Запросе №2 оптимизатор СУБД видит всю картину целиком и может принять решение не сканировать всю таблицу ЭСФ, а использовать индексы для поиска конкретных соответствий.
Современные СУБД умеют выполнять «развертывание» подзапросов. Рассмотрим, как это работает. Когда вы пишете ГДЕ Ссылка В (ВЫБРАТЬ...), система не обязательно сначала выполняет внутренний запрос. Она может преобразовать его во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Если внешняя таблица (ваша реализация) очень маленькая, СУБД просто берет Ссылка из реализации и идет искать её в индексе ЭСФ. Это происходит мгновенно (операция Index Seek), в то время как создание временной таблицы требует полного сканирования (Table Scan).
Рассмотрим важный технический момент: индексирование временной таблицы — это дополнительная нагрузка. Мы часто слышим, что «соединение должно идти по индексированным полям». Это верно, но важно помнить: индекс в 1С создается только после того, как данные уже помещены в ВТ. Если данных много, время на создание индекса перекроет выгоду от его использования в финальном соединении.
Проанализируем финальный вариант решения (сообщение №44), который показал 30-кратное ускорение по сравнению с исходным кодом. Основная идея — максимально обрезать данные на каждом этапе, не допуская попадания лишних строк в промежуточные таблицы.
Разберем алгоритм по шагам:
&Ссылка).СчетФактураПолученный.ДокументыОснования только те записи, которые относятся к нашему списку из шага 1.втЭСФ мы помещаем только те записи, ссылки которых мы нашли на шаге 2.Посмотрим на пример правильной структуры запроса:
// ПАКЕТ 1: Выбираем только те документы, которые указаны в ссылке
ВЫБРАТЬ
Реализация.НомерСтроки,
Реализация.ГТДДокумент КАК ГТДДокумент
ПОМЕСТИТЬ ВТ_Документы
ИЗ
Документ.РеализацияТоваровУслуг.ТоварыПоГТД КАК Реализация
ГДЕ
Реализация.Ссылка = &Ссылка
ИНДЕКСИРОВАТЬ ПО ГТДДокумент;
// ПАКЕТ 2: Находим только СФ, связанные с нашими документами
ВЫБРАТЬ
СФ_Основания.Ссылка КАК Ссылка,
СФ_Основания.ДокументОснование КАК ДокументОснование
ПОМЕСТИТЬ ВТ_СФ
ИЗ
Документ.СчетФактураПолученный.ДокументыОснования КАК СФ_Основания
ГДЕ
СФ_Основания.ДокументОснование В (ВЫБРАТЬ Т.ГТДДокумент ИЗ ВТ_Документы КАК Т);
// ПАКЕТ 3: Выбираем данные ЭСФ только для найденных СФ
ВЫБРАТЬ
ЭСФ.СчетФактура КАК СчетФактура,
ЭСФ.РегистрационныйНомер,
ЭСФ.ПометкаУдаления
ПОМЕСТИТЬ втЭСФ
ИЗ
Документ.ЭСФ КАК ЭСФ
ГДЕ
ЭСФ.СчетФактура В (ВЫБРАТЬ Т.Ссылка ИЗ ВТ_СФ КАК Т)
И НЕ ЭСФ.ПометкаУдаления
И НЕ ЭСФ.Статус В (&СтатусОтозван)
ИНДЕКСИРОВАТЬ ПО СчетФактура;
Почему это работает быстрее?
ЭСФ целиком.ЭСФ.СчетФактура В (ВЫБРАТЬ Т.Ссылка ИЗ ВТ_СФ) позволяет СУБД использовать существующий индекс основной таблицы ЭСФ.Рассмотрим рекомендации, которые помогут избежать ошибок при замере производительности:
НЕ ЭСФ.Статус В (&СтатусОтозван) заставляет систему просматривать лишние данные. Если возможно, замените его на список «позитивных» статусов.JOIN). Для таблиц в 10-20 строк индекс может даже замедлить выполнение.&Ссылка) или перезапускать сеанс.Таким образом, мы выяснили причину «подвоха»: использование временных таблиц без предварительной фильтрации данных превратило их из инструмента ускорения в «якорь», замедляющий систему из-за избыточных дисковых операций.