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