Как правильно оптимизировать запрос к регистру «Дополнительные сведения» при отборе по составному типу?

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

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

Разрушение мифа: нужен ли оператор ВЫРАЗИТЬ в секции ГДЕ?

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


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

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

Физически в СУБД (будь то MS SQL Server или PostgreSQL) поле составного ссылочного типа хранится не в виде связи со всеми таблицами, а в виде нескольких простых колонок в таблице регистра сведений. Обычно это две колонки:

  1. Колонка типа (например, _Fld123_TRef) — хранит шестнадцатеричный идентификатор метаданных (какой именно это справочник или документ).
  2. Колонка уникального идентификатора ссылки (например, _Fld123_RRef) — хранит непосредственно UUID объекта.

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

WHERE T1._Fld123_TRef = 0x000000BA AND T1._Fld123_RRef = @P1

СУБД моментально находит нужную запись, используя существующий индекс по этим колонкам (выполняется операция Index Seek).

Если же вы используете оператор ВЫРАЗИТЬ в секции ГДЕ, платформа 1С вынуждена сгенерировать сложную SQL-конструкцию с оператором CASE WHEN для проверки типа прямо внутри условия фильтрации. Для СУБД такое условие становится невычисляемым по индексу (Non-Sargable). База данных больше не может использовать индекс для быстрого поиска. Вместо этого она будет вынуждена построчно просканировать всю таблицу регистра (выполнить тяжелую операцию Index Scan или Table Scan), вычисляя выражение для каждой записи. На больших базах данных это гарантированно приведет к зависанию запроса и блокировкам таблиц.

Правило: Для простого сравнения составного поля с параметром (Объект = &Параметр) использовать оператор ВЫРАЗИТЬ в секции ГДЕ категорически запрещено!

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

Использовать приведение типов через ВЫРАЗИТЬ нужно только в одном случае: когда вы обращаетесь к реквизитам полей составного типа «через точку» (разыменование).

Например, если вы напишете:

ВЫБРАТЬ ДополнительныеСведения.Объект.Наименование

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

Чтобы этого избежать, мы жестко указываем платформе конкретную таблицу с помощью ВЫРАЗИТЬ:


ВЫБРАТЬ ВЫРАЗИТЬ(ДополнительныеСведения.Объект КАК Справочник.ДоговорыКонтрагентов).Наименование

В этом случае СУБД соединится только с одной таблицей — справочником договоров.

Специфика индексов регистра «Дополнительные сведения»

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

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

Оптимизация поиска по свойству (заголовку или наименованию)

Рассмотрим второе условие в исходном неоптимальном запросе:

ГДЕ ДополнительныеСведения.Свойство.Заголовок = &НаименовниеДопСвед

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

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

  1. Сначала во встроенном языке найдем конкретную ссылку на свойство по его представлению. Платформа очень быстро выполнит эту операцию (часто значение уже закэшировано).
  2. Передадим полученную прямую ссылку на свойство в качестве параметра в наш основной запрос.

Проанализируем оптимальный код получения данных:


// Шаг 1. Находим прямую ссылку на свойство (измерение регистра)
СсылкаНаСвойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "НужноеИмяСвойства");

Если ЗначениеЗаполнено(СсылкаНаСвойство) Тогда

    // Шаг 2. Выполняем максимально облегченный запрос к регистру
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |	ДополнительныеСведения.Значение КАК Значение
        |ИЗ
        |	РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения
        |ГДЕ
        |	ДополнительныеСведения.Объект = &ДокСсылка
        |	И ДополнительныеСведения.Свойство = &Свойство";
        
    Запрос.УстановитьПараметр("ДокСсылка", ОбъектСсылка);
    Запрос.УстановитьПараметр("Свойство", СсылкаНаСвойство);
    
    РезультатЗапроса = Запрос.Выполнить();
    // Обработка результата...
КонецЕсли;

Почему этот запрос идеален для СУБД?

Использование стандартных механизмов БСП

Если вы работаете в любой типовой конфигурации 1С (например, 1С:ERP, 1С:Комплексная автоматизация, 1С:Управление торговлей или 1С:Бухгалтерия), в них уже интегрирована Библиотека Стандартных Подсистем (БСП). Разработчики фирмы «1С» уже написали и оптимизировали все запросы для работы со свойствами.

Вместо написания собственных запросов к регистру сведений настоятельно рекомендуется использовать готовый программный интерфейс БСП общего модуля УправлениеСвойствами.

Для получения значения одного конкретного свойства для одного объекта используйте функцию:


ЗначениеСвойства = УправлениеСвойствами.ЗначениеСвойства(ОбъектСсылка, СсылкаНаСвойство);

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


// На входе массивы объектов и свойств
ТаблицаЗначений = УправлениеСвойствами.ЗначенияСвойств(МассивОбъектов, МассивСвойств);

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

← На главную