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

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

В практике разработки на платформе 1С:Предприятие мы часто сталкиваемся с ситуацией, когда, казалось бы, простой запрос выполняется неоправданно долго. В таких случаях разработчику необходимо провести замер производительности контура 1С и поиск узких мест, чтобы понять причину деградации системы. Одной из самых распространенных причин падения производительности является использование так называемых "неявных соединений" или, проще говоря, обращение к реквизитам ссылочных типов через точку.

В этой статье мы подробно разберем, почему удобный синтаксис Документ.Контрагент.Менеджер.ФизЛицо может "убить" производительность вашей базы данных, проанализируем, как платформа преобразует такие конструкции в SQL-запросы, и научимся писать правильный, оптимизированный код. Мы рассмотрим эту проблему комплексно, основываясь на обсуждениях экспертов и документации.

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

Когда мы пишем код на встроенном языке 1С или составляем текст запроса, платформа предоставляет нам удобный объектный доступ к данным. Например, если нам нужно получить фамилию менеджера, который указан в заказе клиента, рука сама тянется написать:


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

С точки зрения читаемости кода — это идеально. Мы сразу видим логическую цепочку связей. Однако, с точки зрения СУБД (SQL Server, PostgreSQL), такой конструкции не существует. СУБД понимает только таблицы и соединения между ними. Чтобы лучше понимать эти процессы, полезно анализировать SQL сервер глазами 1С-ника, сопоставляя код 1С с итоговым SQL-запросом.

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

В нашем примере цепочка Контрагент.Менеджер.ФизЛицо.Наименование заставит платформу сделать следующее:

  1. Присоединить таблицу справочника Контрагенты к таблице Заказов.
  2. К результату присоединить таблицу справочника Пользователи (если менеджер — это пользователь).
  3. К этому результату присоединить таблицу справочника ФизическиеЛица.

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

Разберем на примере: "Убийца" производительности

Давайте рассмотрим конкретный пример из практики, который часто вызывает проблемы. Допустим, нам нужно выбрать номенклатуру из табличной части документа и показать страну производителя этой номенклатуры.

Неоптимизированный запрос выглядит так:


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

На первый взгляд, все безобидно. Но давайте посчитаем количество неявных соединений. Здесь мы обращаемся к таблице номенклатуры дважды. В старых версиях платформы это могло приводить к двум отдельным соединениям с одной и той же таблицей. В современных версиях оптимизатор платформы стал умнее и может сворачивать одинаковые пути, но риск остается.

Более опасная ситуация возникает, когда мы используем обращение через точку в секции ГДЕ. Посмотрим на такой код:


ВЫБРАТЬ
    Заказ.Ссылка
ИЗ
    Документ.ЗаказКлиента КАК Заказ
ГДЕ
    Заказ.Контрагент.ОсновнойМенеджер.ФизЛицо.Пол = &Мужской

В данном случае СУБД не может использовать индексы эффективно, так как фильтрация происходит по полю, присоединенному через несколько таблиц (levels of indirection). Это часто приводит к полному сканированию таблиц (Table Scan), что на больших объемах данных вызывает "тормоза".

Решение проблемы: Используем явные соединения

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

Давайте перепишем наш первый пример с менеджером, используя явные ЛЕВОЕ СОЕДИНЕНИЕ.


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

Почему этот код лучше?

  1. Контроль типов: При обращении через точку к составному типу данных (например, поле "ДокументОснование", которое может быть разных типов), платформа присоединяет ВСЕ возможные таблицы документов, входящие в этот тип. При явном соединении мы соединяемся только с нужной нам таблицей (используя функцию ВЫРАЗИТЬ или просто соединяясь с конкретным справочником).
  2. Предсказуемость: Мы точно видим структуру запроса и можем управлять ею.
  3. Использование индексов: Явные условия соединения (ПО ...) позволяют SQL-серверу эффективнее использовать индексы.

Важный нюанс: Составные типы данных

Особое внимание уделим реквизитам составного типа. Это классическая ловушка для начинающих программистов 1С. Рассмотрим ситуацию с регистром сведений, где измерение Документ может быть ссылкой на ЗаказКлиента, Реализацию или Возврат.

Если мы напишем:

ВЫБРАТЬ Записи.Документ.Дата ...

Платформа создаст SQL-запрос, в котором будет ЛЕВОЕ СОЕДИНЕНИЕ к каждой из трех таблиц документов! Это генерирует огромный и медленный запрос.

Как исправить? Использовать функцию ВЫРАЗИТЬ (CAST) перед обращением к полю.


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

В этом случае платформа поймет, что нас интересует только таблица ЗаказКлиента, и сделает соединение только с ней. Это критически важно для производительности.

Влияние RLS (Ограничение прав доступа на уровне записей)

Еще один аспект, который мы должны учитывать — это RLS (Row Level Security). Когда в конфигурации включено ограничение прав доступа на уровне записей, каждое "неявное соединение" через точку усложняет проверку прав.

Представим, что у пользователя ограничен доступ к справочнику Контрагенты. Если мы обращаемся Заказ.Контрагент.Наименование, система должна:

  1. Проверить права на чтение Заказа.
  2. Проверить права на чтение конкретного Контрагента (присоединить таблицу настроек прав).

Если цепочка длинная (Заказ.Контрагент.Менеджер.ГруппаДоступа...), то к основному запросу "прицепляются" подзапросы проверки прав для каждой таблицы в цепочке. Это может замедлить выполнение запроса в десятки раз.

При использовании явных соединений мы также не избавляемся от RLS, но структура запроса становится более плоской, и часто оптимизатору СУБД проще построить план выполнения.

Когда допустимо использовать обращение через точку?

Не стоит впадать в крайности и полностью запрещать использование точек. Есть ситуации, когда это оправдано и не несет вреда:

Практический алгоритм оптимизации запроса

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

  1. Откройте текст запроса.
  2. Удобнее всего проводить отладку, если под рукой современная консоль запросов УФ 8.3.2.24.22 (мод от Dr.Zombi), поддерживающая пакетные запросы.
  3. Найдите все конструкции, содержащие более одной точки (например, Таблица.Реквизит.Поле).
  4. Проанализируйте типы данных этих реквизитов. Являются ли они составными?
  5. Замените обращения через точку на секцию ЛЕВОЕ СОЕДИНЕНИЕ (или ВНУТРЕННЕЕ СОЕДИНЕНИЕ, если поле обязательно заполнено).
  6. Для полей составного типа обязательно используйте конструкцию ВЫРАЗИТЬ или фильтр ССЫЛКА.
  7. Сравните время выполнения "до" и "после" на реальном объеме данных.

Дополнительный совет: Использование временных таблиц

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

Стратегия "Разделяй и властвуй":

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

Стоит отметить, что в новых версиях платформы (8.3.25+) появились дополнительные возможности, такие как добавление данных в существующую временную таблицу, что может быть полезно в сложных алгоритмах.


// Шаг 1: Отбираем заказы во временную таблицу
ВЫБРАТЬ
    Заказ.Ссылка КАК Ссылка,
    Заказ.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_Заказы
ИЗ
    Документ.ЗаказКлиента КАК Заказ
ГДЕ
    Заказ.Дата МЕЖДУ &НачалоПериода И &КонецПериода;

ИНДЕКСИРОВАТЬ ПО
    Контрагент;

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

Такой подход (пакетные запросы) часто является "золотой пулей" для сложных отчетов, позволяя обойти ограничения оптимизатора СУБД и получить результат максимально быстро.

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

← На главную