Как оптимизировать запрос RLS и исключить условие ИЛИ для повышения производительности

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

При настройке ограничений доступа на уровне записей (RLS) программисты часто сталкиваются с тем, что использование логического оператора ИЛИ (OR) в условиях запроса приводит к существенным задержкам. Это происходит из-за особенностей работы оптимизаторов СУБД (MS SQL Server, PostgreSQL), которые при наличии OR часто отказываются от использования индексов и переходят к полному сканированию таблицы (Index Scan). Рассмотрим, как можно переписать условие RLS, чтобы сделать запросы максимально быстрыми.

Проблема использования ИЛИ в условиях доступа

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


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

Для СУБД это условие является «тяжелым», так как оно связывает булево поле и результат подзапроса через ИЛИ. Выясним причину: серверу базы данных сложно построить эффективный план выполнения, объединяющий две принципиально разные выборки. В результате система начинает тормозить даже на средних объемах данных.

Метод 1. Использование параметров сеанса для кэширования прав

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

Создадим параметр сеанса ДоступныеЦФО (тип ФиксированныйМассив) и заполним его при начале сеанса или при первом обращении:


// В модуле сеанса или специализированном общем модуле
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ РАЗЛИЧНЫЕ
|	Настройка.ЦФО КАК ЦФО
|ИЗ
|	РегистрСведений.НастройкаДоступаКДоговорамПоРЛС КАК Настройка
|ГДЕ
|	Настройка.Пользователь = &Пользователь";

Запрос.УстановитьПараметр("Пользователь", ПараметрыСеанса.ТекущийПользователь);
ПараметрыСеанса.ДоступныеЦФО = Новый ФиксированныйМассив(Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ЦФО"));

Теперь текст RLS упростится, но условие ИЛИ все еще остается:

ГДЕ ДоступноВсемФилиалам ИЛИ ОсновнойЦФО В (&ДоступныеЦФО)

Метод 2. Денормализация данных (добавление вспомогательного реквизита)

Чтобы полностью избавиться от ИЛИ, мы можем применить архитектурный прием денормализации. Посмотрим на ситуацию под другим углом: и признак «Доступно всем», и конкретный «ЦФО» — это, по сути, идентификаторы доступа.

  1. Добавим в справочник ДоговорыКонтрагентов вспомогательный реквизит КлючДоступа (тип ЛюбаяСсылка или Строка).
  2. При записи договора будем программно заполнять этот реквизит: если установлен флаг ДоступноВсемФилиалам, записываем туда предопределенное значение (например, пустую ссылку или специальный элемент справочника «Общий доступ»). Если флаг не установлен — записываем ОсновнойЦФО.
  3. В параметрах сеанса к списку доступных ЦФО всегда добавляем этот «Общий» идентификатор.

Результат будет выглядеть так:


ДоговорыКонтрагентов ГДЕ 
ДоговорыКонтрагентов.КлючДоступа В (&СписокДоступныхКлючей)

Такое условие идеально ложится на индексы СУБД и работает мгновенно.

Метод 3. Использование механизма «Ключей доступа» (БСП)

Если конфигурация построена на базе «Библиотеки стандартных подсистем» (БСП), рассмотрим самый современный способ — переход на производительный режим RLS. Разберем по шагам, как это работает:

В этом режиме 1С не вычисляет условия «на лету» в каждом запросе. Вместо этого при записи объекта система автоматически формирует записи в специальных регистрах сведений (КлючиДоступаКОбъектам). Логика ИЛИ отрабатывает только один раз — в момент записи договора. SQL-запрос, который в итоге уходит к базе, превращается в простое и быстрое соединение (INNER JOIN) с таблицей прав. Для внедрения этого метода необходимо описать логику доступа в модуле менеджера объекта в процедуре ПриЗаполненииОграниченияДоступа.

Метод 4. Оптимизация через подзапрос с UNION

Иногда платформа позволяет использовать ОБЪЕДИНИТЬ ВСЕ внутри оператора В (ВЫБРАТЬ...). Это позволяет «обмануть» оптимизатор и заставить его выполнить два независимых индексных поиска. Проанализируем пример конструкции:


ДоговорыКонтрагентов ГДЕ 
ДоговорыКонтрагентов.Ссылка В (
    ВЫБРАТЬ Ссылка ИЗ Справочник.ДоговорыКонтрагентов ГДЕ ДоступноВсемФилиалам = ИСТИНА
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ Ссылка ИЗ Справочник.ДоговорыКонтрагентов ГДЕ ОсновнойЦФО В (&ДоступныеЦФО)
)

Важно: Прямое использование UNION в самом тексте RLS может быть ограничено синтаксисом платформы. Однако использование ВЫБОР КОГДА иногда дает похожий эффект, заставляя СУБД вычислять условия последовательно.

Метод 5. Временные таблицы в параметрах сеанса (версии 8.3.17+)

Рассмотрим продвинутый метод для высоконагруженных систем. Начиная с версии платформы 8.3.17, мы можем передавать во временные таблицы параметров сеанса готовые списки разрешенных ссылок или ключей. Это позволяет избежать повторных вычислений прав в сложных иерархиях.

Проанализируем преимущества этого подхода:

Выводы и рекомендации

Выясним, какой метод выбрать в конкретной ситуации. Если ваша база данных невелика, достаточно вынести список разрешенных значений в параметры сеанса (Метод 1). Для крупных систем с миллионами записей лучшим решением будет использование механизма Ключей доступа БСП или денормализация (Метод 2), так как это полностью убирает логическую неопределенность ИЛИ на уровне СУБД, гарантируя стабильную скорость работы индексов.

← На главную