Какой запрос к составному реквизиту в 1С выполнится быстрее и почему

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с реквизитами составного типа (например, Основание, который может ссылаться на любые документы в базе) часто возникает вопрос: как правильно и с максимальной производительностью наложить фильтр в запросе по конкретному документу-основанию. Разберем три распространенных варианта написания условий и проанализируем, как platforma 1С и СУБД транслируют их в реальные SQL-запросы.

Варианты написания условий в запросе

Рассмотрим задачу: необходимо выбрать все документы ЗаказНаряд, у которых в реквизите Основание (составной тип данных) указан конкретный документ ЗаказПокупателя, переданный в параметр &Основание.

Вариант 1 (Явная проверка типа ССЫЛКА + равенство):


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

Вариант 2 (Прямое равенство с параметром):


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

Вариант 3 (Использование ВЫРАЗИТЬ в секции ГДЕ):


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

Анализ работы планов СУБД и замеров производительности

Проанализируем поведение оптимизатора СУБД при выполнении каждого из трех вариантов запроса на реальной базе данных с проиндексированным полем Основание.

  1. Вариант 2 — Оптимальный choice. При передаче в параметр &Основание конкретной ссылки на документ платформа 1С автоматически определяет тип данных этого параметра. Поле составного типа в таблице СУБД состоит из трех колонок: типа (_TYPE), метаданных (_RTRef) и самого идентификатора (_RRRef). При прямом сравнении СУБД выполняет быстрый поиск по индексу (Index Seek), сразу проверяя все три составные части ключа. В плане выполнения СУБД это выглядит как 3 операции чтения.
  2. Вариант 1 — Аналогичен по скорости, но содержит лишний код. Конструкция ЗаказНаряд.Основание ССЫЛКА Документ.ЗаказПокупателя генерирует дублирующую проверку типа на уровне SQL. СУБД выполняет абсолютно тот же поиск по индексу (Index Seek) и те же 3 операции чтения. Условие ССЫЛКА здесь избыточно, так как передаваемый параметр &Основание уже жестко определяет тип документа.
  3. Вариант 3 — Самый медленный вариант (антипаттерн). Использование конструкции ВЫРАЗИТЬ внутри секции ГДЕ транслируется платформой 1С в T-SQL конструкцию вида CASE WHEN ... THEN _RRRef END = @P2. Вычисление функции поверх индексируемой колонки делает данное условие неиндексируемым (non-SARGable). В результате СУБД теряет возможность использовать точечный поиск по индексу и переходит к сплошному сканированию индекса или таблицы (Index Scan), рассчитывая выражение CASE для каждой строки в таблице. Число чтений при этом возрастает с единиц до тысяч.

Механика хранения составных типов в СУБД

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

  1. _TYPE — тип данных (например, бинарный код, определяющий ссылку, строку, число и т. д.);
  2. _RTRef — уникальный идентификатор таблицы конкретного объекта метаданных;
  3. _RRRef — GUID (16 байт) конкретной ссылки.

Когда мы пишем условие ЗаказНаряд.Основание = &Основание, платформа передает значение параметра раскладом по этим трем полям. В результате СУБД сразу выполняет точечную выборку по составному индексу по триаде полей (_TYPE, _RTRef, _RRRef).

Когда действительно необходимо применять ВЫРАЗИТЬ

Использование конструкции ВЫРАЗИТЬ категорически не рекомендуется в секциях ГДЕ и ПО при сравнении значений. Однако ВЫРАЗИТЬ выполняет важнейшую функцию в других случаях:

  1. При разыменовании полей составного типа ("получение реквизита через точку"). Если написать ЗаказНаряд.Основание.Дата, платформа 1С сделает неявные левые соединения (LEFT JOIN) со всеми таблицами документов, входящими в этот составной тип. Если типов десятки, запрос критически замедлится. Ограничить соединения поможет ВЫРАЗИТЬ:
    
    ВЫБРАТЬ
    	ВЫРАЗИТЬ(ЗаказНаряд.Основание КАК Документ.ЗаказПокупателя).Дата КАК ДатаЗаказа
    ИЗ
    	Документ.ЗаказНаряд КАК ЗаказНаряд
    
  2. При помещении данных составного типа во временную таблицу. Если поле составного типа помещается во временную таблицу для последующих соединений, его необходимо ограничить с помощью ВЫРАЗИТЬ прямо в секции ВЫБРАТЬ, чтобы типизировать колонку временной таблицы и избежать распространения неявных соединений на последующие запросы пакета.

Итоги и рекомендации

Выяснив причины работы оптимизатора СУБД, сформулируем правила работы с реквизитами составных типов:

  1. Для фильтрации по конкретной ссылке передавайте ссылку в параметр и используйте Вариант 2 (прямое равенство Поле = &Параметр). Это самый чистый и быстрый код.
  2. Избегайте использования ВЫРАЗИТЬ в секции ГДЕ, чтобы не провоцировать сплошной Index Scan на стороне СУБД.
  3. Не забывайте взводить флаг Индексировать на реквизите составного типа в конфигураторе, если по нему планируется часто производить отбор документов.
← На главную