Как оптимизировать медленный запрос к регистру сведений при использовании составных типов данных

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

В практике разработки на платформе 1С:Предприятие часто возникает ситуация, когда, казалось бы, простой запрос к небольшому регистру сведений начинает «тормозить» систему. Особенно остро это проявляется в таких конфигурациях, как 1С:Комплексная автоматизация 1.1 или УПП, и на файловых базах данных. Разберем на конкретном примере, почему обращение к полю Ссылка через точку может замедлить работу программы в несколько раз и как правильно оптимизировать такие запросы.

Проблема неявных соединений (Implicit Joins)

Рассмотрим исходную ситуацию: в системе создан самодельный регистр сведений НетДокументов, где измерение Документ имеет составной тип данных (например, ссылки на несколько видов документов). Автор темы столкнулся с тем, что запрос в подписке на событие ПриЗаписи выполняется крайне медленно. Проанализируем исходный код запроса:


Запрос.Текст ="ВЫБРАТЬ РАЗРЕШЕННЫЕ
              |    НетДокументов.Документ.Ссылка КАК Док
              |ИЗ
              |    РегистрСведений.НетДокументов КАК НетДокументов
              |ГДЕ
              |    НетДокументов.Документ.Ссылка = &Документ";

Основная причина низкой производительности здесь заключается в конструкции НетДокументов.Документ.Ссылка. Для платформы 1С обращение через точку от поля составного типа является командой на выполнение неявного левого соединения (LEFT OUTER JOIN) со всеми таблицами документов, которые входят в этот тип. Если измерение Документ может принимать значения 10 различных видов документов, СУБД будет вынуждена соединить таблицу регистра с 10 таблицами документов, чтобы проверить наличие ссылки.

Важно понимать: поле Документ само по себе уже содержит ссылку (GUID). Обращение к Документ.Ссылка избыточно и заставляет систему выполнять лишнюю работу по поиску данных в физических таблицах документов.

Решение 1: Устранение обращений через точку

Для оптимизации необходимо обращаться напрямую к полю измерения. Выясним причину: когда мы пишем НетДокументов.Документ, система берет значение непосредственно из таблицы регистра. Проанализируем исправленный вариант запроса:


Запрос.УстановитьПараметр("Документ", СсылкаНаДокумент);
Запрос.Текст ="ВЫБРАТЬ РАЗРЕШЕННЫЕ
              |    НетДокументов.Документ КАК Док
              |ИЗ
              |    РегистрСведений.НетДокументов КАК НетДокументов
              |ГДЕ
              |    НетДокументов.Документ = &Документ";

В данном случае платформа сравнивает идентификаторы (GUID) напрямую в таблице регистра. Это позволяет СУБД эффективно использовать индекс по измерению Документ, не затрагивая другие таблицы базы данных. Как отметил автор темы, такое простое изменение позволило ускорить выполнение операции в два раза.

Решение 2: Использование Менеджера записи вместо Запроса

Если логика работы в подписке на событие подразумевает проверку существования только одной конкретной записи, использование объекта Запрос может быть не самым оптимальным путем. Разберем альтернативный подход с использованием МенеджераЗаписи. Этот метод особенно эффективен в файловых базах, так как он работает напрямую с индексом первичного ключа.

Посмотрим на пример реализации:


МенеджерЗаписи = РегистрыСведений.НетДокументов.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Документ = Источник.Ссылка; // Источник - документ из подписки
МенеджерЗаписи.Прочитать();

Если Не МенеджерЗаписи.Выбран() Тогда
    // Записи нет, можно создавать новую
    МенеджерЗаписи.Документ = Источник.Ссылка;
    МенеджерЗаписи.ЕстьДокумент = Ложь;
    МенеджерЗаписи.Записать();
КонецЕсли;

Метод Прочитать() работает быстрее, чем создание, парсинг и выполнение текста запроса, так как платформа сразу обращается к нужной строке по известным измерениям.

Оптимизация при групповой обработке данных

Часто проблема «медленного запроса» проявляется не при работе пользователя в форме, а при групповом перепроведении документов или восстановлении последовательностей. Если подписка на событие содержит запрос, то при проведении 1000 документов система выполнит 1000 отдельных запросов к базе данных.

Для оптимизации таких процессов проанализируем следующие рекомендации:

  1. Проверка признака загрузки: Если документ проводится в режиме обмена данными, проверку часто можно пропустить, используя ОбменДанными.Загрузка = Истина.
  2. Вынос запроса из цикла: Если вы пишете свою обработку, старайтесь получить данные по всем нужным документам одним запросом заранее и поместить их в таблицу значений или соответствие для быстрого поиска в памяти.
  3. Привилегированный режим: Если в системе настроены ограничения прав на уровне записей (RLS), каждый запрос будет дополняться сложными условиями проверки прав. Выполнение кода в привилегированном модуле позволит избежать накладных расходов на RLS, что значительно ускорит работу.

Влияние файлового режима и блокировок

В файловой базе данных 1С (формат .1CD) при записи любого объекта блокируется вся таблица. Если запрос внутри транзакции записи выполняется долго (из-за тех же неявных соединений), это блокирует работу других пользователей. Посмотрим, как это происходит: подписка ПриЗаписи выполняется в той же транзакции, что и запись самого документа. Чем дольше работает ваш «простой» запрос, тем дольше висит блокировка на всей таблице документов. Поэтому оптимизация каждого запроса в таких событиях критически важна для стабильности системы.

Подведем итоги: для обеспечения максимальной производительности при работе с регистрами сведений необходимо:

  1. Никогда не обращаться через точку к полям составного типа в условиях ГДЕ и секции ВЫБРАТЬ, если вам нужна только сама ссылка.
  2. Использовать МенеджерЗаписи для точечных проверок и модификаций.
  3. Минимизировать использование ВЫБРАТЬ РАЗРЕШЕННЫЕ в коде, который выполняется при проведении, перенося логику в привилегированные модули.
  4. Всегда проверять производительность через Замер производительности в конфигураторе, чтобы найти «узкое место» в коде.
← На главную