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

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

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

Механизм разыменования полей: что происходит «под капотом»

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


ВЫБРАТЬ
    ТЧ.Контрагент.Ссылка,
    ТЧ.Контрагент.Регион,
    ТЧ.Контрагент.Менеджер,
    ТЧ.ВидСкидки,
    ТЧ.Скидка
ИЗ
    Документ.УстановкаСкидок.ТЧ КАК ТЧ

В данном случае платформа создаст скрытое левое соединение с основной таблицей справочника Контрагенты. Если в запросе встречается несколько обращений через точку к одному и тому же объекту, современные версии платформы стараются оптимизировать это в одно соединение, но в сложных вложенных запросах или при использовании устаревших релизов может возникнуть ситуация, когда на каждое обращение будет создано отдельное соединение в SQL-тексте. Проанализируем, почему явное прописывание связи в секции ЛЕВОЕ СОЕДИНЕНИЕ является более надежным подходом.

Проблема составных типов данных и «взрыв» соединений

Самая критичная проблема разыменования связана с полями составного типа. Представим, что поле Контрагент может содержать ссылку не только на справочник Контрагенты, но и на справочник ФизическиеЛица. Если мы напишем ТЧ.Контрагент.Наименование, платформа будет вынуждена соединить таблицу ТЧ со всеми таблицами, которые входят в этот составной тип. Это происходит потому, что на этапе компиляции запроса 1С не знает, какой именно тип будет лежать в конкретной строке.

Если в составной тип входит 10 справочников, одно обращение через точку породит 10 соединений (JOIN) в SQL. Это приводит к следующим негативным последствиям:

  1. Раздувание SQL-текста: Запрос становится огромным, его сложнее кэшировать СУБД.
  2. Превышение лимитов СУБД: В MS SQL Server существует жесткое ограничение — не более 256 таблиц в одном запросе. При глубоком разыменовании составных полей (например, в регистрах через поле Регистратор) этот лимит достигается мгновенно, и запрос завершается ошибкой.
  3. Падение производительности: Оптимизатору СУБД становится крайне сложно построить эффективный план выполнения для запроса с десятками лишних соединений.

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


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

В этом случае платформа поймет, что соединение нужно строить только с одной таблицей — Справочник.Контрагенты.

Явное задание плана запроса и предсказуемость

Когда мы пишем ЛЕВОЕ СОЕДИНЕНИЕ вручную, мы жестко определяем структуру запроса. Это делает код прозрачным для других разработчиков и для самой системы. Выясним, почему это важно для оптимизации плана запроса:

Во-первых, при явном соединении мы можем точно указать условия связи в секции ПО. Это позволяет оптимизатору SQL (особенно в PostgreSQL) лучше понять зависимости между данными. В некоторых случаях использование «точек» в секции ГДЕ заставляет СУБД выполнять полное сканирование таблиц вместо использования индексов.

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

Влияние на PostgreSQL и специфику планов выполнения

Проанализируем работу запросов в среде PostgreSQL. В отличие от MS SQL Server, оптимизатор Postgres очень чувствителен к статистике и глубине вложенности. При использовании неявных соединений (через точку) велика вероятность того, что оптимизатор выберет метод Nested Loop (вложенный цикл) там, где эффективнее был бы Hash Join.

Явное указание связей позволяет разработчику контролировать последовательность соединения таблиц. Если мы сначала выбираем данные во временную таблицу, а затем соединяем ее с основной таблицей справочника, мы помогаем СУБД корректно оценить количество строк и выбрать оптимальный индекс.

Читаемость кода и стандарты разработки 1С

Существует субъективный, но важный критерий — читаемость. Посмотрим на два варианта написания одного и того же блока:

  1. ТЧ.Контрагент.Менеджер.Наименование КАК ИмяМенеджера
  2. Менеджеры.Наименование КАК ИмяМенеджера (при условии явного соединения с алиасом Менеджеры)

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

Рекомендации по использованию временных таблиц

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

  1. Выбираем только необходимые ссылочные поля (например, Ссылка контрагента) во временную таблицу.
  2. Индексируем временную таблицу по этой ссылке.
  3. В финальном запросе соединяем временную таблицу с основной таблицей справочника для получения дополнительных реквизитов (Регион, Менеджер и т.д.).

Такой подход минимизирует нагрузку на базу данных tempdb и позволяет СУБД максимально эффективно использовать индексы. Посмотрим на пример такой реализации:


// Помещаем данные во временную таблицу без лишних реквизитов
ВЫБРАТЬ
    ТЧ.Контрагент КАК Контрагент,
    СУММА(ТЧ.Сумма) КАК СуммаОборота
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
СГРУППИРОВАТЬ ПО
    ТЧ.Контрагент;

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

Итоги: когда и что выбирать?

Подведем итоги нашего анализа. Выясним основные правила:

Соблюдение этих правил обеспечит стабильность ваших запросов, легкость их сопровождения и высокую скорость работы системы даже при росте объема данных или изменении метаданных конфигурации.

← На главную