Оптимизация запросов
Общие принципы
- Выбирайте только нужные поля, избегайте
ВЫБРАТЬ *. - Не усложняйте запросы без необходимости. Слишком сложные запросы (особенно с подзапросами и множеством таблиц) могут привести к неоптимальным планам выполнения от СУБД.
- Не пытайтесь любой ценой перенести всю логику в СУБД. Иногда проще постобработать данные на стороне 1С или использовать несколько простых запросов вместо одного сложного.
Индексы и условия отбора
Ключевое правило: Условия в ГДЕ, ПО соединения и параметрах виртуальных таблиц должны полностью совпадать с началом существующего индекса, поля должны идти подряд.
- Автоматические индексы создаются платформой (по ссылке, регистратору, периоду+измерениям и т.д).
- Дополнительные индексы создавайте осознанно, только для частых и критичных запросов.
- Не индексируйте низкоселективные поля (например,
Булево, если нет сильного перекоса в значениях).
Пример проблемы: В регистре измерения [Склад, Номенклатура, Качество]. Запрос с отбором по Номенклатура, но без Склад не сможет использовать индекс эффективно, так как нарушено требование "поля подряд".
Решение: Создать дополнительный индекс или (осторожно) изменить порядок измерений.
Работа с составными типами
Главная проблема: Обращение к полю через точку (Документ.Регистратор.Дата) для поля составного типа приводит к неявному соединению со ВСЕМИ таблицами возможных типов (например, со всеми видами документов). Это резко замедляет запрос.
Решение:
- Избегайте таких обращений в условиях отбора и выборки.
- Используйте функцию
ВЫРАЗИТЬ(), если заранее известен конкретный тип. - Дублируйте данные (например, добавьте в регистр реквизит
ДатаРегистратора). - Уточняйте составной тип в метаданных, не используйте "любаяСсылка" без необходимости.
Временные таблицы и подзапросы
- Избегайте соединений (
ПО) с вложенными запросами. Это часто приводит к ошибочным планам и нестабильной производительности. - Замените соединение с подзапросом на соединение с временной таблицей. Это упрощает жизнь оптимизатору СУБД.
- Избегайте вложенных соединений. Переписывайте их на последовательные, если логика позволяет.
- Не используйте вложенные запросы в условии соединения (
ПО ... В (ПОДЗАПРОС)). Выносите логику во временные таблицы.
Пример: Получение "последней цены" через ПО ... В (ВЫБРАТЬ МАКСИМУМ(Период) ...) лучше делать в два шага через временную таблицу.
Виртуальные таблицы
- Всегда передавайте условия отбора в ПАРАМЕТРЫ виртуальной таблицы, а не в
ГДЕосновного запроса....Остатки(, Склад = &Склад)(ПРАВИЛЬНО)...Остатки() ГДЕ Склад = &Склад(НЕПРАВИЛ ЬНО)
- В параметрах виртуальных таблиц используйте только простые условия (
Измерение = Значение). Избегайте подзапросов и обращений через точку. - Для остатков на текущую дату не указывайте дату в параметрах таблицы
Остатки(). - Чтобы получить остатки эффективно, используйте все измерения регистра в основном запросе (в
ВЫБРАТЬили вПОсоединения).
Эффективные условия в запросах
- Основное условие (которое сильно сокращает выборку) должно использовать индекс и содержать только операции, по которым возможен поиск по индексу:
=,В,>,<,ПОДОБНО "Нач%",МЕЖДУ. - Оператор
ИЛИв основном условии допустим только для последнего поля индекса и если его можно заменить наВ. - Функции (
ПОДСТРОКА(),МЕСЯ Ц()) и арифметические операции в условиях не позволяют использовать индекс. Переписывайте:ПОДСТРОКА(Поле,1,3) = "АБВ"->Поле ПОДОБНО "АБВ%"МЕСЯЦ(Дата) = 1->Дата МЕЖДУ ДАТАВРЕМЯ(2023,01,01) И ДАТАВРЕМЯ(2023,01,31,23,59,59)
- Конструкцию
ВЫБОРиспользуйте только в дополнительных условиях в секцииГДЕ.
Временные таблицы
- Индексируйте большие временные таблицы, участвующие в соединениях или условиях
В(...). - Не помещайте во временную таблицу лишние поля или записи.
- Не создавайте временные таблицы в цикле. Создайте одну до цикла.
- Избегайте временных таблиц с огромным числом записей (сотни тысяч). Работайте порциями.
Прочие важные советы
- RLS (Ограничения доступа): Назначайте пользователю только одну роль с правами на чтение объекта, иначе условия
ИЛИв запросе могут убить производительность. Учитывайте RLS при проектировании запросов. - Разрешение итогов для регистров сведений: Включайте только если:
- Очень много данных.
- Частые запросы к
СрезПоследних()без указания даты. - Условия в виртуальной таблице - только по измерениям.
- Используйте
РАЗРЕШЕННЫЕпри работе в системе с включенным RLS. - Вычисления делайте в
ВЫБРАТЬ, а не вГДЕ.
Ответы на ключевые вопросы по запросам
1. Как оптимально вытащить реквизит из поля составного типа?
Использовать типизацию через ВЫРАЗИТЬ. Дополнительно ограничить в условие ГДЕ Регистратор ССЫЛКА Документ.ИмяДокумента
2. Отличия полного соединения от внутреннего?
- Внутреннее: Возвращает только совпадающие строки из обеих таблиц
- Полное: Возвращает все строки из обеих таблиц (NULL при отсутствии совпадений)
3. Что оптимальнее: обход выборки или выгрузка в ТЗ?
Обход выборки. Таблица значений хранится в оперативной памяти → при больших объемах можно получить ошибку "Недостаточно памяти".
4. Для чего нужна конструкция РАЗРЕШЕННЫЕ?
Позволяет исключить записи, к которым у пользователя нет прав доступа (работает с RLS).
5. Можно ли использовать запросы в цикле?
Не рекомендуется для регулярных операций. Запрос нужно выносить за цикл. Исключение — порционная выборка больших данных.
6. Как пронумеровать строки в выборке?
Использовать АВТОНОМЕРЗАПИСИ:
7. Как повторно обойти выборку результата запроса?
Вызвать Выборка.Сбросить() для сброса указателя на начало.
8. Для чего индексировать временные таблицы?
Для ускорения соединений с большими ВТ. Оправдано, если:
- ВТ очень большая
- Используется в множественных соединениях
- Передается через
МенеджерВременныхТаблиц
9. Почему Регистратор.Дата неоптимально?
Поле Регистратор составного типа → неявное соединение со всеми таблицами возможных типов. Решение: Вынести дату в отдельный реквизит или использовать ВЫРАЗИТЬ.
10. Где можно накладывать условия отбора?
В ГДЕ, условиях соединения (ПО), параметрах виртуальных таблиц, ИМЕЮЩИЕ.
11. Как обойти выборку с условием "Сумма = 5000"?
Использовать Выборка.НайтиСледующий() с отбором по условию.
12. Три основные ошибки в запросах, влияющие на производительность:
- Обращение через точку к полям составного типа
- Использование
ИЛИв основном условии отбора - Соединение с подзапросами вместо временных таблиц
13. Как поместить таблицу значений в запрос?
- Описать ТЗ в запросе с типизацией полей
- Передать как параметр через
ПОМЕСТИТЬ
14. Как оптимально получить склад из регистратора-документа?
ВЫБРАТЬ
ВЫРАЗИТЬ(Регистратор КАК Документ.ПоступлениеТоваров).Склад
ИЗ РегистрНакопления
ГДЕ Регистратор ССЫЛКА Документ.ПоступлениеТоваров
15. Принцип получения курсов валют на каждый день:
- Временная таблица для отбора дат (производственный календарь в типовых)
- Соединяем с этой таблицей физичесткую таблицу курса валют. Соединение ДатаМесяц >= КурсПериод. Максимум КурсПериод.
- Вторую временную таблицу соединяем с физической таблицей валют внутренним соединеием по валюте и периоду
16. Где хранится временная таблица?
В tempdb СУБД.
17. Как получить первую запись в регистре?
ВЫБРАТЬ ПЕРВЫЕ 1
18. Как получить документы РТУ с >5 номенклатур?
ИМЕЮЩИЕ КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Номенклатура) > 5