При написании запросов в платформе 1С:Предприятие часто используется обращение к реквизитам ссылочных полей через точку (например, Документ.Договор.Владелец или Продажи.Договор.Организация). Такое разыменование полей удобно при написании кода, однако на уровне СУБД оно превращается в неявные левые соединения. При работе с большими объемами данных или реквизитами составного типа это может привести к существенному падению производительности. Разберем, как устроена работа обращений через точку, какие риски они несут и какими способами можно от них отказаться.
Когда в секции ВЫБРАТЬ или в других частях запроса выполняется обращение к реквизиту связанной таблицы через точку, платформа 1С автоматически подставляет неявное левое соединение (LEFT JOIN) с соответствующей физической таблицей базы данных.
Рассмотрим, как количество «точек» влияет на сложность итогового SQL-запроса:
Реализация.Договор.Код): создается одно неявное левое соединение с таблицей справочника договоров. Если тип реквизита однозначный (не составной), это не создает критической нагрузки, и оптимизатор СУБД легко справляется с такой задачей.Документ.Договор.Владелец.Наименование): платформе приходится построить цепочку последовательных левых соединений. Каждое дополнительное соединение усложняет план выполнения запроса.Наиболее тяжелые последствия возникают при обращении через точку к реквизитам составного типа. Если поле может содержать ссылки на несколько различных типов объектов (например, реквизит Регистратор или измерение СчетФактура, содержащее ссылки на 10–15 видов документов), платформа не знает заранее, какой именно объект хранится в конкретной строке.
В этом случае на уровне SQL-запроса платформа 1С вынуждена выполнять соединение со всеми физическими таблицами, входящими в этот составной тип, а выбор конкретного значения происходит с помощью конструкции CASE WHEN. В результате вместо одного легкого соединения генерируются десятки LEFT JOIN, что критически замедляет выполнение запроса (особенно в динамических списках и высоконагруженных отчетах).
Если необходимо обратиться к реквизиту поля составного типа, но в конкретном контексте заранее известен точный тип объекта, следует применить функцию ВЫРАЗИТЬ(). Она сообщает платформе, к какой именно таблице нужно выполнить соединение, исключая все остальные варианты.
Посмотрим на пример ограничения составного типа СчетФактура в регистре накопления:
ВЫБРАТЬ
ВЫРАЗИТЬ(Регистр.СчетФактура КАК Документ.ПоступлениеТоваровУслуг).НомерВходящегоДокумента КАК НомерВходДок
ИЗ
РегистрНакопления.НДСЗаписиКнигиПокупок КАК Регистр
Если поле составного типа может содержать 2–3 конкретных вида документов или справочников, вместо множественных неявных соединений эффективнее использовать ветвление с проверкой типа с помощью ключевого слова ССЫЛКА:
ВЫБРАТЬ
ВЫБОР
КОГДА Источник.Заказ ССЫЛКА Документ.ЗаказПокупателя
ТОГДА ВЫРАЗИТЬ(Источник.Заказ КАК Документ.ЗаказПокупателя).Номер
КОГДА Источник.Заказ ССЫЛКА Документ.ЗаказПоставщику
ТОГДА ВЫРАЗИТЬ(Источник.Заказ КАК Документ.ЗаказПоставщику).Номер
ИНАЧЕ ""
КОНЕЦ КАК НомерЗаказа
ИЗ
Документ.РеализацияТоваровУслуг КАК Источник
Такой подход позволяет платформе построить ветвление на уровне SQL только для указанных видов объектов, избегая соединения с остальными таблицами составного типа.
Самый прозрачный и управляемый способ избежать неопределенности — отказаться от разыменования полей в пользу явного написания секции ЛЕВОЕ СОЕДИНЕНИЕ.
Проанализируем ситуацию, когда необходимо получить контрагента из договора документа:
ВЫБРАТЬ
Реализация.Ссылка КАК ДокументСсылка,
Договоры.Владелец КАК Контрагент
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ДоговорыКонтрагентов КАК Договоры
ПО Реализация.ДоговорКонтрагента = Договоры.Ссылка
Преимущества явного соединения очевидны:
Помимо прямого влияния на количество соединений, обращение к реквизитам через точку может вызывать незаметные на первый взгляд проблемы:
ГДЕ: Разыменование поля в секции ВЫБРАТЬ создает LEFT JOIN. Однако если то же поле с точкой используется в условии ГДЕ (например, ГДЕ Реализация.Договор.ВидДоговора = &Вид), СУБД фактически превращает такое соединение во внутреннее (INNER JOIN). Если в документе не заполнен договор, вся строка целиком выпадет из итоговой выборки.Table Scan).Для сложных и объёмных запросов с большим количеством связей наилучшим решением является двухэтапная обработка данных с помощью временных таблиц:
ИНДЕКСИРОВАТЬ ПО.Рассмотрим пример реализации такого подхода:
// Шаг 1: Подготовка уникальных ключей связывания
ВЫБРАТЬ РАЗЛИЧНЫЕ
Реализация.ДоговорКонтрагента КАК Договор
ПОМЕСТИТЬ ВТ_УникальныеДоговоры
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Дата МЕЖДУ &ДатаНач И &ДатаКон
ИНДЕКСИРОВАТЬ ПО
Договор
;
// Шаг 2: Получение реквизитов и явное соединение
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
Договоры.Владелец КАК Контрагент
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_УникальныеДоговоры КАК ВТ_Договоры
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ДоговорыКонтрагентов КАК Договоры
ПО ВТ_Договоры.Договор = Договоры.Ссылка
ПО Реализация.ДоговорКонтрагента = ВТ_Договоры.Договор
Такой подход гарантирует оптимизатору СУБД точную статистику, исключает повторные тяжелые соединения с физическими таблицами и обеспечивает максимальную скорость выполнения запроса.