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