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

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

В среде разработчиков 1С часто можно услышать о «смертном грехе» — обращении к реквизитам через точку в текстах запросов. Существует мнение, что это всегда ведет к деградации производительности и созданию неоптимальных планов запросов на стороне SQL-сервера. В данной статье мы подробно разберем, так ли это на самом деле, проанализируем внутренние механизмы платформы и выясним, в каких ситуациях использование «точек» допустимо, а в каких — категорически запрещено.

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

Когда мы пишем в запросе конструкцию вида ФактическоеРабочееВремяСотрудников.Сотрудник.Магазин, платформа 1С:Предприятие не передает этот текст в СУБД в исходном виде. Вместо этого она выполняет трансформацию запроса, заменяя обращение через точку на неявное левое соединение (LEFT OUTER JOIN). Рассмотрим это на примере из обсуждения:


ВЫБРАТЬ
    ФактическоеРабочееВремяСотрудников.Сотрудник КАК Сотрудник
ИЗ
    РегистрНакопления.ФактическоеРабочееВремяСотрудников КАК ФактическоеРабочееВремяСотрудников
ГДЕ
    ФактическоеРабочееВремяСотрудников.Сотрудник.Магазин = &Магазин

В данном случае, если поле Сотрудник имеет простой ссылочный тип (например, СправочникСсылка.ФизическиеЛица), платформа сформирует SQL-запрос, в котором таблица регистра будет соединена с таблицей справочника физических лиц по полю Ссылка. Проанализируем ситуацию: если тип поля одиночный (не составной), то такое обращение практически идентично явному левому соединению, написанному вручную. СУБД (например, MS SQL Server) в большинстве случаев построит один и тот же план выполнения для обоих вариантов.

Главная опасность: Составные типы данных

Ситуация кардинально меняется, когда мы обращаемся через точку к полю составного типа. Классический пример — поле Регистратор в регистрах накопления или бухгалтерии. Если в качестве регистраторов указано, скажем, 20 различных видов документов, то при написании Регистратор.Номер платформа будет вынуждена выполнить соединение со всеми 20 таблицами этих документов.

Рассмотрим, почему это критично:

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

Как отмечалось в обсуждении, обращение регОбороты.Регистратор.Комментарий — это прямой путь к серьезным проблемам с производительностью, если список регистраторов велик.

Использование оператора ВЫРАЗИТЬ для оптимизации

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


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

В этом случае платформа создаст LEFT JOIN только с одной таблицей — документом «Реализация товаров и услуг». Все остальные типы документов будут проигнорированы, что значительно ускорит выполнение запроса.

Явное соединение против «точки»

Выясним, когда стоит отказаться от точек в пользу явного СОЕДИНЕНИЯ в секции ИЗ. Рассмотрим запрос, предложенный в теме для сравнения:


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

Преимущества явного соединения:

Точки в параметрах виртуальных таблиц

Особое внимание следует уделить условиям в параметрах виртуальных таблиц (например, СрезПоследних или Обороты). Написание условия типа Сотрудник.Магазин = &Магазин внутри параметров виртуальной таблицы — это грубая ошибка. Платформа сначала выберет данные из регистра, а уже потом будет накладывать фильтр по магазину через соединение. Правильнее сначала получить список ссылок сотрудников нужного магазина во временную таблицу, а затем передать её в параметры виртуальной таблицы через условие Сотрудник В (ВЫБРАТЬ Ссылка ИЗ ВТ_СотрудникиМагазина).

Практические рекомендации

Подведем итог и сформулируем правила «хорошего тона» при написании запросов:

  1. Для простых ссылочных типов: Использование одной-двух точек допустимо в простых выборках, если это не виртуальная таблица. Однако, если вы планируете проходить аудит (например, для 1С:Fresh), лучше сразу использовать явные соединения.
  2. Для составных типов: Использование точек без ВЫРАЗИТЬ запрещено. Всегда ограничивайте тип данных.
  3. Для высоконагруженных систем: При работе с таблицами, содержащими миллионы строк, заменяйте любые неявные соединения (точки) на явные ВНУТРЕННЕЕ СОЕДИНЕНИЕ или использование временных таблиц.
  4. Денормализация: Если вам постоянно требуется фильтровать регистр по реквизиту измерения (например, по магазину сотрудника), рассмотрите возможность добавления этого реквизита в само измерение регистра (как обсуждалось в сообщениях 40 и 50). Это позволит СУБД использовать индекс регистра напрямую без обращения к справочнику.
  5. Реестры документов: При необходимости часто выбирать общие реквизиты разных документов (Дата, Номер, Сумма, Комментарий), используйте вспомогательные регистры сведений (например, «Реестр документов» из БСП), чтобы избежать тяжелых соединений с множеством таблиц.

Соблюдение этих правил позволит вам писать не только работающий, но и быстрый код, который не вызовет проблем при масштабировании информационной системы.

← На главную