Skip to main content

Оптимизация запросов

Общие принципы

  • Выбирайте только нужные поля, избегайте ВЫБРАТЬ *.
  • Не усложняйте запросы без необходимости. Слишком сложные запросы (особенно с подзапросами и множеством таблиц) могут привести к неоптимальным планам выполнения от СУБД.
  • Не пытайтесь любой ценой перенести всю логику в СУБД. Иногда проще постобработать данные на стороне 1С или использовать несколько простых запросов вместо одного сложного.

Индексы и условия отбора

Ключевое правило: Условия в ГДЕ, ПО соединения и параметрах виртуальных таблиц должны полностью совпадать с началом существующего индекса, поля должны идти подряд.

  • Автоматические индексы создаются платформой (по ссылке, регистратору, периоду+измерениям и т.д).
  • Дополнительные индексы создавайте осознанно, только для частых и критичных запросов.
  • Не индексируйте низкоселективные поля (например, Булево, если нет сильного перекоса в значениях).

Пример проблемы: В регистре измерения [Склад, Номенклатура, Качество]. Запрос с отбором по Номенклатура, но без Склад не сможет использовать индекс эффективно, так как нарушено требование "поля подряд".

Решение: Создать дополнительный индекс или (осторожно) изменить порядок измерений.

Работа с составными типами

Главная проблема: Обращение к полю через точку (Документ.Регистратор.Дата) для поля составного типа приводит к неявному соединению со ВСЕМИ таблицами возможных типов (например, со всеми видами документов). Это резко замедляет запрос.

Решение:

  1. Избегайте таких обращений в условиях отбора и выборки.
  2. Используйте функцию ВЫРАЗИТЬ(), если заранее известен конкретный тип.
  3. Дублируйте данные (например, добавьте в регистр реквизит ДатаРегистратора).
  4. Уточняйте составной тип в метаданных, не используйте "любаяСсылка" без необходимости.

Временные таблицы и подзапросы

  • Избегайте соединений (ПО) с вложенными запросами. Это часто приводит к ошибочным планам и нестабильной производительности.
  • Замените соединение с подзапросом на соединение с временной таблицей. Это упрощает жизнь оптимизатору СУБД.
  • Избегайте вложенных соединений. Переписывайте их на последовательные, если логика позволяет.
  • Не используйте вложенные запросы в условии соединения (ПО ... В (ПОДЗАПРОС)). Выносите логику во временные таблицы.

Пример: Получение "последней цены" через ПО ... В (ВЫБРАТЬ МАКСИМУМ(Период) ...) лучше делать в два шага через временную таблицу.

Виртуальные таблицы

  1. Всегда передавайте условия отбора в ПАРАМЕТРЫ виртуальной таблицы, а не в ГДЕ основного запроса.
    • ...Остатки(, Склад = &Склад) (ПРАВИЛЬНО)
    • ...Остатки() ГДЕ Склад = &Склад (НЕПРАВИЛЬНО)
  2. В параметрах виртуальных таблиц используйте только простые условия (Измерение = Значение). Избегайте подзапросов и обращений через точку.
  3. Для остатков на текущую дату не указывайте дату в параметрах таблицы Остатки().
  4. Чтобы получить остатки эффективно, используйте все измерения регистра в основном запросе (в ВЫБРАТЬ или в ПО соединения).

Эффективные условия в запросах

  • Основное условие (которое сильно сокращает выборку) должно использовать индекс и содержать только операции, по которым возможен поиск по индексу: =, В, >, <, ПОДОБНО "Нач%", МЕЖДУ.
  • Оператор ИЛИ в основном условии допустим только для последнего поля индекса и если его можно заменить на В.
  • Функции (ПОДСТРОКА(), МЕСЯЦ()) и арифметические операции в условиях не позволяют использовать индекс. Переписывайте:
    • ПОДСТРОКА(Поле,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. Три основные ошибки в запросах, влияющие на производительность:

  1. Обращение через точку к полям составного типа
  2. Использование ИЛИ в основном условии отбора
  3. Соединение с подзапросами вместо временных таблиц

13. Как поместить таблицу значений в запрос?

  1. Описать ТЗ в запросе с типизацией полей
  2. Передать как параметр через ПОМЕСТИТЬ

14. Как оптимально получить склад из регистратора-документа?

ВЫБРАТЬ 
ВЫРАЗИТЬ(Регистратор КАК Документ.ПоступлениеТоваров).Склад
ИЗ РегистрНакопления
ГДЕ Регистратор ССЫЛКА Документ.ПоступлениеТоваров

15. Принцип получения курсов валют на каждый день:

  1. Временная таблица для отбора дат (производственный календарь в типовых)
  2. Соединяем с этой таблицей физичесткую таблицу курса валют. Соединение ДатаМесяц >= КурсПериод. Максимум КурсПериод.
  3. Вторую временную таблицу соединяем с физической таблицей валют внутренним соединеием по валюте и периоду

16. Где хранится временная таблица?

В tempdb СУБД.

17. Как получить первую запись в регистре?

ВЫБРАТЬ ПЕРВЫЕ 1

18. Как получить документы РТУ с >5 номенклатур?

ИМЕЮЩИЕ КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Номенклатура) > 5

19. Минус обращения через точку к составному типу:

Соединение со всеми таблицами возможных типов → резкое падение производительности.

20. На какую дату остатки, если не указать дату?

Актуальные остатки (на 01.01.3999).

21. Разница между ГДЕ и ИМЕЮЩИЕ:

  • ГДЕ — фильтрация до группировки (отбор строк)
  • ИМЕЮЩИЕ — фильтрация после группировки (отбор групп)

22. Разница ОБЪЕДИНИТЬ и ОБЪЕДИНИТЬ ВСЕ:

  • ОБЪЕДИНИТЬудаляет дубли автоматически
  • ОБЪЕДИНИТЬ ВСЕ — сохраняет все строки, включая дубли

23. Почему не выгружать в ТЗ для обхода?

ТЗ хранится в оперативной памяти → риск исчерпания памяти при больших объемах данных.

24. Какой SQL запрос строится при обращении к таблице остатков?

Сложный запрос с использованием итоговых таблиц и досчетом по движениям, если период не совпадает с итогами.

25. Что лучше: дата документа или момент времени?

Момент времени точнее, если важны секунды. Для обычных операций достаточно даты. Отличие: МоментВремени включает время с точностью до секунд.

26. Как изменить запрос с "ИЛИ" и зачем?

Заменить на ОБЪЕДИНИТЬ ВСЕ или МЕЖДУ, если возможно. ИЛИ часто мешает использованию индексов.

27. Почему не смешивать составные и простые типы?

Для каждого составного типа создаются отдельные колонки в БД → сложная структура, трудности с индексами и отборами.

28. Как получить изменения объектов на узле плана обмена?

Выбирать таблицу Изменения: ИмяОсновнойТаблицы.Изменения

ВЫБРАТЬ
НоменклатураИзменения.НомерСообщения КАК НомерСообщения,
НоменклатураИзменения.Ссылка КАК Ссылка
ИЗ
Справочник.Номенклатура.Изменения КАК НоменклатураИзменения
ГДЕ
НоменклатураИзменения.Узел = &Узел

29. Что будет при МАКСИМУМ(МоментВремени)?

Ошибка "Неправильные параметры", т.к. МоментВремени — виртуальное поле, вычисляемое при выполнении.

30. Почему Выборка.Ссылка.Наименование неоптимально?

Создает неявный запрос, загружающий весь объект со всеми табличными частями и реквизитами.

31.Когда не удалять временные таблицы?

Когда не используется МенеджерВременныхТаблиц и точно известно, что ВТ с таким именем больше не создастся в сеансе.

Материалы