При работе с большими объемами данных в 1С (миллионы документов или записей в регистрах) стандартные подходы к написанию запросов могут привести к существенным задержкам. Часто разработчики сталкиваются с ситуацией, когда запрос, работавший быстро на тестовой базе, начинает "тормозить" в реальных условиях. Разберем по шагам, как оптимизировать такой запрос, используя рекомендации опытных специалистов.
В представленном примере кода мы видим классический случай неоптимального построения запроса. Основные проблемы заключаются в следующем:
ОстаткиИОбороты, данные собираются по частям из Остатки и Обороты, что увеличивает количество обращений к базе данных.Период там, где в итоге происходит группировка по лицевому счету, создает ненужную нагрузку.Рассмотрим первый блок запроса, где мы выбираем список лицевых счетов из документов. Хотя автор утверждает, что этот кусок работает быстро, при росте количества документов до 6 миллионов он станет "узким местом".
Проанализируем ситуацию: получение данных напрямую из документов — это ресурсозатратная операция. Если бизнес-логика позволяет, такие данные лучше хранить в регистрах сведений. Однако, если мы вынуждены работать с документами, необходимо убедиться, что реквизиты Ответственный и ЛицевойСчет проиндексированы в метаданных. Также важно правильно типизировать данные.
ВЫБРАТЬ РАЗЛИЧНЫЕ
Событие.ЛицевойСчет КАК ЛицевойСчет
ПОМЕСТИТЬ СписокЛС
ИЗ
Документ.Событие КАК Событие
ГДЕ
Событие.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И НЕ Событие.ПометкаУдаления
И Событие.Ответственный В(&СписокСотрудников)
И Событие.ЛицевойСчет <> ЗНАЧЕНИЕ(Справочник.ЛицевыеСчета.ПустаяСсылка)
ИНДЕКСИРОВАТЬ ПО
ЛицевойСчет
;
Важное изменение: мы добавили инструкцию ИНДЕКСИРОВАТЬ ПО ЛицевойСчет. Это критически важно для всех последующих соединений с этой временной таблицей. Без индекса SQL-сервер будет выполнять полное сканирование таблицы СписокЛС при каждом обращении.
Посмотрим на конструкцию ЛицевойСчет В (ВЫБРАТЬ СписокЛС.ЛицевойСчет ИЗ СписокЛС) внутри параметров виртуальных таблиц. Это типичный анахронизм. Платформе 1С гораздо проще работать, когда в параметр передается просто имя временной таблицы или готовый массив.
Рассмотрим подробнее, как правильно передавать условия в виртуальные таблицы. Вместо вложенного запроса мы будем использовать саму временную таблицу в условиях связи или предварительно выгруженные массивы. Но самый эффективный способ — фильтрация внутри параметров &Условие виртуальной таблицы.
Вместо четырех отдельных выборок (сальдо на начало, сальдо на конец, начисления и оплата) через ОБЪЕДИНИТЬ ВСЕ, мы можем использовать одну таблицу РегистрНакопления.РасчетыСАбонентами.ОстаткиИОбороты. Это позволит СУБД получить все данные за один проход по таблице итогов и движений.
Разберем, как будет выглядеть запрос с использованием этой таблицы:
ВЫБРАТЬ
Расчеты.ЛицевойСчет КАК ЛицевойСчет,
ЕСТЬNULL(Расчеты.СуммаНачальныйОстаток, 0) КАК СальдоНачальное,
ЕСТЬNULL(Расчеты.СуммаКонечныйОстаток, 0) КАК СальдоКонечное,
ЕСТЬNULL(Расчеты.СуммаПриход, 0) КАК Начисления,
ЕСТЬNULL(Расчеты.СуммаРасход, 0) КАК Оплата
ПОМЕСТИТЬ РасчетыИтого
ИЗ
РегистрНакопления.РасчетыСАбонентами.ОстаткиИОбороты(
&НачалоПериода,
&КонецПериода,
Авто,
,
ЛицевойСчет В (ВЫБРАТЬ СписокЛС.ЛицевойСчет ИЗ СписокЛС)) КАК Расчеты
;
Обратите внимание на использование ЕСТЬNULL. При левых соединениях или работе с виртуальными таблицами могут возникать значения NULL, которые "испортят" арифметические операции и итоги в отчете. Мы всегда приводим числовые показатели к нулю.
В исходном запросе автора использовалась функция ДОБАВИТЬКДАТЕ(&ОкончаниеПериода, СЕКУНДА, 1). Это попытка захватить движения, сделанные в последнюю секунду периода. Однако в 1С для этих целей существует более элегантное и точное решение — объект Граница.
Проанализируем ситуацию: если документ записан в 23:59:59, обычное указание даты может его не подхватить (зависит от настроек виртуальной таблицы). В СКД мы можем настроить параметры так, чтобы использовалось выражение:
Новый Граница(&КонецПериода, ВидГраницы.Включая)
Это гарантирует, что все движения, включая те, что попали на последнюю секунду, будут учтены корректно без "приплюсовывания" лишней секунды, которая может захватить документы следующего дня.
Соберем все рекомендации воедино. Мы избавились от вложенных подзапросов, внедрили индексацию и объединили получение данных в одну виртуальную таблицу.
// 1. Формируем индексированный список ЛС
ВЫБРАТЬ РАЗЛИЧНЫЕ
Событие.ЛицевойСчет КАК ЛицевойСчет
ПОМЕСТИТЬ ВТ_СписокЛС
ИЗ
Документ.Событие КАК Событие
ГДЕ
Событие.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И НЕ Событие.ПометкаУдаления
И Событие.Ответственный В(&СписокСотрудников)
И Событие.ЛицевойСчет <> ЗНАЧЕНИЕ(Справочник.ЛицевыеСчета.ПустаяСсылка)
ИНДЕКСИРОВАТЬ ПО
ЛицевойСчет
;
////////////////////////////////////////////////////////////////////////////////
// 2. Получаем все данные одним пакетом
ВЫБРАТЬ
ВТ_СписокЛС.ЛицевойСчет КАК ЛицевойСчет,
ЕСТЬNULL(Взаиморасчеты.СуммаНачальныйОстаток, 0) КАК СальдоНачальное,
ЕСТЬNULL(Взаиморасчеты.СуммаКонечныйОстаток, 0) КАК СальдоКонечное,
ЕСТЬNULL(Начисления.СуммаОборот, 0) КАК Начисления,
ЕСТЬNULL(Взаиморасчеты.СуммаРасход, 0) КАК Оплата
ИЗ
ВТ_СписокЛС КАК ВТ_СписокЛС
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РасчетыСАбонентами.ОстаткиИОбороты(
&НачалоПериода,
&КонецПериода,
Авто,
,
ЛицевойСчет В (ВЫБРАТЬ ВТ_СписокЛС.ЛицевойСчет ИЗ ВТ_СписокЛС)) КАК Взаиморасчеты
ПО ВТ_СписокЛС.ЛицевойСчет = Взаиморасчеты.ЛицевойСчет
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.НачисленияАбонентскойПлаты.Обороты(
&НачалоПериода,
&КонецПериода,
Авто,
ЛицевойСчет В (ВЫБРАТЬ ВТ_СписокЛС.ЛицевойСчет ИЗ ВТ_СписокЛС)) КАК Начисления
ПО ВТ_СписокЛС.ЛицевойСчет = Начисления.ЛицевойСчет
Выясним причину ускорения:
ЛицевойСчет во временной таблице, SQL-сервер сможет мгновенно находить нужные строки при соединении с виртуальными таблицами регистров.ОБЪЕДИНИТЬ ВСЕ, который часто вызывает неявную сортировку и нагружает временную базу данных сервера.ЛицевойСчет В (...)) позволяет платформе использовать индексы самого регистра (Index Seek), а не перебирать все записи (Index Scan).Совет: Если вы используете СКД, старайтесь не писать сложные запросы вручную в тексте запроса набора данных. Используйте механизмы компоновки, они часто умеют оптимизировать простые соединения автоматически, добавляя нужные фильтры в виртуальные таблицы на лету.