Перейти к содержимому

7.4 Для повышения скорости выполнения запроса с условием необходимо

Опубликовано: Обновлено:

Telegram → Просто о платформе 1С

❓ 7.4 Для повышения скорости выполнения запроса с условием (в случае, когда это не противоречит прикладной логике) необходимо:


Общий принцип оптимизации — обрабатывать меньше данных

Заголовок раздела «Общий принцип оптимизации — обрабатывать меньше данных»

Одна из базовых рекомендаций при написании запросов — стараться минимизировать объем выборки, то есть получать запросом только те данные, которые действительно нужны.

Например, если для решения задачи нужны остатки только по одной номенклатуре, желательно по возможности ограничить данные этой номенклатурой как можно раньше, а не выполнять лишнюю обработку данных, которые затем будут отброшены.

Но оптимизация — это не попытка любой ценой выполнить всю задачу одним запросом. Слишком сложный запрос может, наоборот, ухудшить ситуацию: оптимизатору СУБД становится сложнее выбрать эффективный план его выполнения.

Поэтому стандарты рекомендует:

📐 Стандарт разработки: #std729

  • выбирать только необходимые данные;
  • писать запросы и условия соединения как можно проще;
  • особенно осторожно использовать подзапросы в соединениях;
  • не использовать без необходимости большое количество источников — отдельно отмечаются запросы с более чем 5–7 таблицами;
  • не строить запросы, размер результата которых может неограниченно расти вместе с информационной базой.

Для обработки потенциально неограниченного объема данных рекомендуется получать данные по частям (курсорный механизм):

  1. установить уникальный порядок сортировки;
  2. получить часть записей с помощью ПЕРВЫЕ;
  3. использовать данные последней полученной записи как начальное условие следующей выборки.

Чтобы понять, почему место условия может влиять на производительность, сначала разберемся с виртуальными таблицами.

Данные информационной базы физически хранятся в таблицах базы данных. Но физические таблицы СУБД и таблицы, которые платформа предоставляет разработчику в языке запросов 1С, — не одно и то же.

Через язык запросов мы можем обращаться к данным прикладных объектов. Например:

  • Справочники
  • Документы
  • Табличные части
  • Регистры

Например, к данным справочника можно обратиться так:

ИЗ
Справочник.Номенклатура КАК Номенклатура

а к записям регистра накопления:

ИЗ
РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах

При этом физическая структура хранения данных платформы значительно шире.

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

Упрощенно:

                Физические данные
                 информационной базы
                         │
             ┌───────────┴───────────┐
             ↓                       ↓
   Данные представлены         Внутренние данные,
   в языке запросов 1С         к которым нет прямого
                               обращения из языка
                               запросов 1С
             │                       │
             ↓                       ↓
      Справочники              Таблицы итогов
      Документы                Служебные таблицы
      Регистры                 и другие внутренние
      Табличные части          структуры хранения

Но при решении прикладных задач нам как раз требуется получать рассчитанные данные — например, остатки или обороты регистра, — не разбираясь самостоятельно, из каких физических таблиц их необходимо получить.

Для этого платформа предоставляет виртуальные таблицы.

Например, для регистра накопления могут быть доступны:

РегистрНакопления.ТоварыНаСкладах.Остатки
РегистрНакопления.ТоварыНаСкладах.Обороты
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты

Виртуальная таблица не является отдельной физической таблицей базы данных.

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

Например, при получении остатков платформа может использовать как таблицу итогов, так и таблицу движений регистра.

Упрощенно:

Запрос разработчика

РегистрНакопления.ТоварыНаСкладах.Остатки(...)
                         ↓
                       1С
                         ↓
        ┌────────────────┴────────────────┐
        ↓                                 ↓
  Таблицы итогов                  Таблица движений
        │                                 │
        └────────────────┬────────────────┘
                         ↓
                 Данные Остатки()

То есть разработчику не требуется самостоятельно определять, из каких физических таблиц и каким способом получить остаток. Эту работу выполняет платформа.

Виртуальную таблицу поэтому можно представить как своеобразный шаблон формирования запроса к физическим данным, но это именно упрощенная модель для понимания механизма, а не отдельная таблица, существующая в СУБД.

Допустим, требуется получить остаток только по одной номенклатуре.

Можно написать условие в секции ГДЕ:

ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура.Наименование КАК Номенклатура,
ТоварыНаСкладахОстатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки() КАК ТоварыНаСкладахОстатки
ГДЕ
ТоварыНаСкладахОстатки.Номенклатура = &Номенклатура

В этом случае условие из секции ГДЕ не участвует в формировании виртуальной таблицы. Сначала формируются данные виртуальной таблицы Остатки(), а затем к полученному результату применяется условие ГДЕ.

Упрощенно:

Таблицы базы данных
        ↓
Формирование виртуальной таблицы Остатки()
        ↓
Данные виртуальной таблицы
        ↓
ГДЕ
Номенклатура = &Номенклатура
        ↓
Результат

То есть с точки зрения логики запроса платформа сначала получает данные виртуальной таблицы, а затем выполняется отбор этих данных по условию ГДЕ.

У виртуальной таблицы условие можно указать непосредственно в ее параметрах:

ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура.Наименование КАК Номенклатура,
ТоварыНаСкладахОстатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(
,
Номенклатура = &Номенклатура
) КАК ТоварыНаСкладахОстатки

Теперь платформа получает информацию об условии при формировании виртуальной таблицы.

Таблицы базы данных
        ↓
Номенклатура = &Номенклатура
        ↓
Платформа формирует данные Остатки()
        ↓
Результат

Именно поэтому в документации рекомендуется использовать параметры виртуальных таблиц вместо отбора в секции ГДЕ, когда это возможно.

Платформа получает условие раньше и может учитывать его при формировании обращения к данным виртуальной таблицы.

Еще одно место, где хорошо виден принцип «отбрасывать ненужные данные раньше», — разница между секциями ГДЕ и ИМЕЮЩИЕ.

Обе конструкции задают условия, но применяются на разных этапах обработки данных.

ГДЕ отбирает записи до группировки.

ИМЕЮЩИЕ используется для отбора уже сгруппированных данных, то есть после вычисления группировок и агрегатных функций.

Например:

ВЫБРАТЬ
Продажи.Номенклатура,
СУММА(Продажи.Количество) КАК Количество
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Склад = &Склад
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
ИМЕЮЩИЕ
СУММА(Продажи.Количество) > 100

Здесь выполняются два принципиально разных отбора:

Записи регистра
        ↓
ГДЕ Склад = &Склад
        ↓
Остаются записи нужного склада
        ↓
СГРУППИРОВАТЬ ПО Номенклатура
        ↓
Считается СУММА(Количество)
        ↓
ИМЕЮЩИЕ СУММА(Количество) > 100
        ↓
Остаются нужные группы

Условие по складу относится к исходным записям, поэтому оно может быть применено до группировки.

Условие СУММА(Количество) > 100 относится уже к результату группировки. До выполнения группировки такого значения еще нет, поэтому этот отбор выполняется через ИМЕЮЩИЕ.

КонструкцияК чему применяетсяКогда
Параметры виртуальной таблицыК данным, формируемым виртуальной таблицейПри формировании виртуальной таблицы
ГДЕК исходным строкам результата источниковДо группировки
СГРУППИРОВАТЬ ПОФормирует группы и позволяет вычислить агрегатыПосле отбора ГДЕ
ИМЕЮЩИЕК результатам группировкиПосле группировки

Если собрать рассмотренные этапы вместе, упрощенную логическую последовательность можно представить так:

Источники данных 
        ↓
Формирование виртуальных таблиц с учетом их параметров
        ↓
Соединение источников
        ↓
ГДЕ
отбор исходных записей
        ↓
СГРУППИРОВАТЬ ПО
формирование групп
        ↓
Вычисление агрегатных значений
        ↓
ИМЕЮЩИЕ
отбор полученных групп
        ↓
Формирование выбранных полей
        ↓
УПОРЯДОЧИТЬ ПО
        ↓
Результат запроса

Это логическая схема, помогающая понять смысл конструкций языка запросов. Она не означает, что СУБД обязана физически выполнять операции строго в такой последовательности: оптимизатор может преобразовывать план выполнения, сохраняя результат запроса.

Но именно такая модель хорошо показывает общий принцип оптимизации:

Параметры виртуальной таблицы
              ↓
            ГДЕ
              ↓
       СГРУППИРОВАТЬ ПО
              ↓
          ИМЕЮЩИЕ
              ↓
          Результат

Чем раньше можно корректно
исключить ненужные данные,
тем меньше данных требуется
обрабатывать дальше

Почему нельзя просто переносить любое условие

Заголовок раздела «Почему нельзя просто переносить любое условие»

Из этого не следует, что любое условие нужно механически переносить на более ранний этап.

Каждая конструкция имеет собственный смысл:

  • параметры виртуальной таблицы влияют на формирование ее данных;
  • ГДЕ отбирает строки до группировки;
  • ИМЕЮЩИЕ отбирает результат группировки.

Перенос условия между этими этапами может изменить результат запроса.

Именно поэтому рекомендация по оптимизации применяется только тогда, когда более раннее наложение условия не противоречит прикладной логике.

Кроме того, само наличие условия еще не гарантирует одинаковый выигрыш в производительности. Необходимо учитывать структуру данных и возможность эффективного применения отбора.

Для регистров, в частности, имеет значение возможность эффективного использования условий отбора при обращении к данным. Эти особенности подробнее разберем дальше, в вопросах раздела «Оперативный учет».

В итоге здесь работает тот же общий принцип, с которого мы начали:

Нужна только часть данных
        ↓
Можно корректно исключить
ненужные данные раньше?
        ↓
       Да
        ↓
Ограничиваем данные
как можно раньше
        ↓
Меньше данных участвует
в следующих операциях запроса

При оптимизации запроса следует стремиться как можно раньше сократить объем обрабатываемых данных, не изменяя при этом прикладную логику. Условия виртуальной таблицы участвуют в формировании ее данных, ГДЕ отбирает записи до группировки, а ИМЕЮЩИЕ — уже полученные группы.