Как правильно и оптимально обратиться к реквизиту реквизита составного типа в запросе 1С?

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

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

Почему обращение через точку к составному типу вызывает проблемы

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

Когда мы обращаемся через одну точку к реквизиту составного поля (например, Источник.СоставноеПоле.Реквизит), платформа не знает заранее, какой именно тип объекта хранится в каждой строке. Поэтому на уровне SQL формируется неявное левое соединение (LEFT JOIN) со всеми таблицами метаданных, типы которых входят в состав реквизита.

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

Включение ограничений прав на уровне записей (RLS) усугубляет ситуацию, так как к каждой автоматически присоединенной таблице СУБД добавляет внутренние подзапросы безопасности. В секциях ГДЕ, ПО и УПОРЯДОЧИТЬ ПО такое разыменование полностью блокирует использование индексов и переводит запрос в режим полного сканирования таблиц.

Вариант 1. Ограничение типа с помощью функции ВЫРАЗИТЬ

Если нам достоверно известно, что в рамках конкретной выборки нас интересует строго определенный ссылочный тип данных, наилучший способ исключить лишние неявные соединения — использовать конструкцию ВЫРАЗИТЬ() КАК. Посмотрим на пример кода:


ВЫБРАТЬ
    ВЫРАЗИТЬ(Источник.СоставноеПоле КАК Документ.РеализацияТоваровУслуг).Контрагент.ИНН КАК ИННКонтрагента
ИЗ
    РегистрНакопления.Взаиморасчеты КАК Источник

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

Вариант 2. Проверка соответствия типу через оператор ССЫЛКА и ВЫБОР КОГДА

Если поле может содержать разные типы ссылок, и нам требуется обработать несколько вариантов или безопасно получить данные, не вызывая ошибок, разберем подход с оператором ССЫЛКА. Посмотрим на пример:


ВЫБРАТЬ
    ВЫБОР
        КОГДА Источник.СоставноеПоле ССЫЛКА Документ.РеализацияТоваровУслуг
            ТОГДА ВЫРАЗИТЬ(Источник.СоставноеПоле КАК Документ.РеализацияТоваровУслуг).Контрагент.ИНН
        КОГДА Источник.СоставноеПоле ССЫЛКА Документ.ПоступлениеТоваровУслуг
            ТОГДА ВЫРАЗИТЬ(Источник.СоставноеПоле КАК Документ.ПоступлениеТоваровУслуг).Контрагент.ИНН
        ИНАЧЕ NULL
    КОНЕЦ КАК ИННКонтрагента
ИЗ
    РегистрНакопления.Взаиморасчеты КАК Источник

Обратите внимание: даже внутри конструкции ВЫБОР КОГДА обязательно нужно применять ВЫРАЗИТЬ, иначе платформа попытается неявно соединить все потенциальные таблицы для ветки разыменования.

Вариант 3. Использование явных соединений (LEFT JOIN)

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


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

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

Вариант 4. Разделение запросов через ОБЪЕДИНИТЬ ВСЕ

Когда требуется выбрать реквизиты второго уровня для нескольких типов документов из большой таблицы, наивысшую производительность обеспечивает разделение задачи на независимые части с последующим объединением. Рассмотрим, как это сделать:


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

ОБЪЕДИНИТЬ ВСЕ

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

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

Вариант 5. Использование временных таблиц с предварительной фильтрацией

Если объем данных в выборке велик, разберем по шагам алгоритм с временными таблицами:

  1. Отбираем во временную таблицу только необходимые ссылки составного поля, отфильтровав их оператором ССЫЛКА.
  2. Индексируем временную таблицу по ссылочному полю.
  3. Во втором пакете запроса выполняем явное соединение временной таблицы с конечными таблицами метаданных.

ВЫБРАТЬ РАЗЛИЧНЫЕ
    Источник.СоставноеПоле КАК ДокументСсылка
ПОМЕСТИТЬ ВТ_Ссылки
ИЗ
    РегистрНакопления.Взаиморасчеты КАК Источник
ГДЕ
    Источник.СоставноеПоле ССЫЛКА Документ.РеализацияТоваровУслуг
ИНДЕКСИРОВАТЬ ПО
    ДокументСсылка
;

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

Архитектурные рекомендации по оптимизации

Помимо оптимизации языка запросов, проанализируем архитектурные методы решения проблемы:

  1. Сужение типов в метаданных: Проведите ревизию состава типов реквизита в дереве метаданных конфигурации. Исключите устаревшие или ошибочно включенные типы (особенно избегайте типа ЛюбаяСсылка, если в этом нет жесткой необходимости). Каждое исключение типа уменьшает число потенциальных соединений в базе.
  2. Денормализация данных: Согласно стандартам разработки 1С, если реквизит объекта второго уровня (например, организация, подразделение или контрагент документа-регистратора) регулярно используется в отчетах и запросах регистра, его следует вынести отдельным реквизитом прямо в регистр и заполнять при проведении. Это позволяет обращаться к полю напрямую, полностью минуя сложные цепочки разыменования.
← На главную