Что выбрать в запросе 1С: обращение к полям через точку или явное соединение?

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

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

Как работает разыменование полей через точку на уровне СУБД

Рассмотрим, что происходит, когда мы пишем обращение через точку в тексте запроса. Для платформы 1С ссылочное поле в базе данных представляет собой шестнадцатеричный уникальный идентификатор (GUID). Сами данные объекта (его реквизиты) физически хранятся в отдельной таблице СУБД.

Когда платформа транслирует запрос 1С в реальный SQL-запрос для СУБД (Microsoft SQL Server, PostgreSQL и др.), любое обращение через точку автоматически преобразуется в неявное ЛЕВОЕ СОЕДИНЕНИЕ (LEFT OUTER JOIN). Таким образом, магии не происходит: платформа за разработчика генерирует ту же самую конструкцию соединения с таблицей объекта.

Посмотрим на исходный пример запроса с точкой:


ВЫБРАТЬ
    ЗаказыПокупателейОбороты.Заказ.ДатаПоступления КАК ДатаПоступления
ИЗ
    РегистрНакопления.ЗаказыПокупателей.Обороты(
        ,
        ,
        ,
        Контрагент = &Контрагент
            И ДоговорКонтрагента = &ДоговорКонтрагента) КАК ЗаказыПокупателейОбороты

И сравним его с вариантом через явное левое соединение:


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

Если реквизит Заказ имеет однозначный ссылочный тип (ссылается только на документ Документ.ЗаказыПокупателей), то на уровне СУБД оба этих запроса сгенерируют практически идентичный SQL-код.

Почему явное соединение не сканирует всю таблицу документов

Распространенное заблуждение начинающих разработчиков заключается в том, что явное соединение заставляет СУБД последовательно перебирать абсолютно все документы в базе, проверяя условие ПО Таблица1.Поле = Таблица2.Ссылка. Выясним причину, почему это не так.

Поле Ссылка у любого справочника, документа или плана видов характеристик является первичным ключом (Primary Key) и по умолчанию имеет кластерный индекс. При выполнении соединения по полю Ссылка СУБД использует быстрый точечный поиск по B-дереву индекса (операция Index Seek), а не полное сканирование таблицы (Table Scan / Index Scan). Это позволяет СУБД находить связанную запись за считанные доли миллисекунды независимо от общего объема документов в базе данных.

Главная опасность точки: составные ссылочные типы

Ситуация кардинально меняется, если поле имеет составной тип данных (например, измерение или реквизит может хранить ссылки на несколько видов документов или справочников). Проанализируем, что произойдет в этом случае:

  1. При обращении через точку: Платформа 1С не знает заранее, ссылка какого типа окажется в конкретной строке таблицы. Поэтому платформа вынуждена сгенерировать неявные ЛЕВЫЕ СОЕДИНЕНИЯ со всеми таблицами объектов, входящими в составной тип. Если составной тип включает 15 видов документов, запрос превратится в 15 тяжелых соединений, что колоссально увеличит нагрузку на оптимизатор СУБД и память сервера.
  2. При явном соединении: Мы явно указываем, с какой конкретно таблицей нужно соединиться, отсекая все лишние проверки.

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


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

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

Ограничения обращения через точку в условиях и фильтрах

Еще одна важная причина предпочесть явное соединение — наложение условий в секции ГДЕ. Рассмотрим разницу:

  1. Разыменование в условии ГДЕ: Написание условий вида ГДЕ Заказ.Контрагент = &Контрагент генерирует неявное левое соединение, а само условие накладывается на итоговый результат уже после соединения. Это часто мешает СУБД эффективно отфильтровать строки на раннем этапе выполнения плана.
  2. Явное соединение: Позволяет использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN), которое автоматически отсекает пустые, незаполненные и битые ссылки, работая в разы быстрее левого соединения. Кроме того, условия фильтрации можно вынести непосредственно в блок ПО (ON), что оптимизирует план запроса.
  3. Каскадные обращения: Конструкции с множественными точками вида Заказ.Контрагент.ОсновнойБанковскийСчет.НомерСчета создают длинную цепочку вложенных левых соединений, что гарантированно снижает производительность на больших объемах данных.

Оптимизация при многократных обращениях (временные таблицы)

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

  1. Выбираем уникальный список ссылок с помощью конструкции ВЫБРАТЬ РАЗЛИЧНЫЕ во временную таблицу.
  2. Индексируем временную таблицу с помощью директивы ИНДЕКСИРОВАТЬ ПО.
  3. Соединяем подготовленную временную таблицу с таблицей объекта и за один проход получаем все необходимые поля.

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

Подведем итоги выбора между точкой и соединением:

  1. Для полей с однозначным (простым) типом обращение через точку в списке выборки допустимо и эквивалентно ЛЕВОМУ СОЕДИНЕНИЮ.
  2. Для полей с составным типом обращение через точку без ВЫРАЗИТЬ() является грубой ошибкой, приводящей к соединению со всеми таблицами типов.
  3. Если требуется отсечь пустые ссылки или наложить жесткий фильтр — всегда используйте явное ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
  4. Для получения данных из табличных частей обращение через точку синтаксически запрещено — здесь применяется только явное соединение.
← На главную