При настройке ограничений доступа на уровне записей (RLS) программисты часто сталкиваются с тем, что использование логического оператора ИЛИ (OR) в условиях запроса приводит к существенным задержкам. Это происходит из-за особенностей работы оптимизаторов СУБД (MS SQL Server, PostgreSQL), которые при наличии OR часто отказываются от использования индексов и переходят к полному сканированию таблицы (Index Scan). Рассмотрим, как можно переписать условие RLS, чтобы сделать запросы максимально быстрыми.
Проанализируем классическую ситуацию: нам нужно разрешить доступ к договору, если у него либо установлен признак ДоступноВсемФилиалам, либо текущий пользователь указан в настройках доступа для конкретного ЦФО. Исходный запрос выглядит так:
ДоговорыКонтрагентов ГДЕ
(ДоговорыКонтрагентов.ДоступноВсемФилиалам
ИЛИ ДоговорыКонтрагентов.ОсновнойЦФО В
(ВЫБРАТЬ Настройка.ЦФО
ИЗ РегистрСведений.НастройкаДоступаКДоговорамПоРЛС КАК Настройка
ГДЕ Настройка.Пользователь = &АвторизованныйПользователь))
Для СУБД это условие является «тяжелым», так как оно связывает булево поле и результат подзапроса через ИЛИ. Выясним причину: серверу базы данных сложно построить эффективный план выполнения, объединяющий две принципиально разные выборки. В результате система начинает тормозить даже на средних объемах данных.
Первым шагом к оптимизации является вынос подзапроса из текста RLS. Обращение к физической таблице регистра сведений внутри каждого запроса к договорам — это избыточная нагрузка. Разберем, как подготовить данные заранее.
Создадим параметр сеанса ДоступныеЦФО (тип ФиксированныйМассив) и заполним его при начале сеанса или при первом обращении:
// В модуле сеанса или специализированном общем модуле
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Настройка.ЦФО КАК ЦФО
|ИЗ
| РегистрСведений.НастройкаДоступаКДоговорамПоРЛС КАК Настройка
|ГДЕ
| Настройка.Пользователь = &Пользователь";
Запрос.УстановитьПараметр("Пользователь", ПараметрыСеанса.ТекущийПользователь);
ПараметрыСеанса.ДоступныеЦФО = Новый ФиксированныйМассив(Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ЦФО"));
Теперь текст RLS упростится, но условие ИЛИ все еще остается:
ГДЕ ДоступноВсемФилиалам ИЛИ ОсновнойЦФО В (&ДоступныеЦФО)
Чтобы полностью избавиться от ИЛИ, мы можем применить архитектурный прием денормализации. Посмотрим на ситуацию под другим углом: и признак «Доступно всем», и конкретный «ЦФО» — это, по сути, идентификаторы доступа.
ДоговорыКонтрагентов вспомогательный реквизит КлючДоступа (тип ЛюбаяСсылка или Строка).ДоступноВсемФилиалам, записываем туда предопределенное значение (например, пустую ссылку или специальный элемент справочника «Общий доступ»). Если флаг не установлен — записываем ОсновнойЦФО.ЦФО всегда добавляем этот «Общий» идентификатор.Результат будет выглядеть так:
ДоговорыКонтрагентов ГДЕ
ДоговорыКонтрагентов.КлючДоступа В (&СписокДоступныхКлючей)
Такое условие идеально ложится на индексы СУБД и работает мгновенно.
Если конфигурация построена на базе «Библиотеки стандартных подсистем» (БСП), рассмотрим самый современный способ — переход на производительный режим RLS. Разберем по шагам, как это работает:
В этом режиме 1С не вычисляет условия «на лету» в каждом запросе. Вместо этого при записи объекта система автоматически формирует записи в специальных регистрах сведений (КлючиДоступаКОбъектам). Логика ИЛИ отрабатывает только один раз — в момент записи договора. SQL-запрос, который в итоге уходит к базе, превращается в простое и быстрое соединение (INNER JOIN) с таблицей прав. Для внедрения этого метода необходимо описать логику доступа в модуле менеджера объекта в процедуре ПриЗаполненииОграниченияДоступа.
Иногда платформа позволяет использовать ОБЪЕДИНИТЬ ВСЕ внутри оператора В (ВЫБРАТЬ...). Это позволяет «обмануть» оптимизатор и заставить его выполнить два независимых индексных поиска. Проанализируем пример конструкции:
ДоговорыКонтрагентов ГДЕ
ДоговорыКонтрагентов.Ссылка В (
ВЫБРАТЬ Ссылка ИЗ Справочник.ДоговорыКонтрагентов ГДЕ ДоступноВсемФилиалам = ИСТИНА
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ Ссылка ИЗ Справочник.ДоговорыКонтрагентов ГДЕ ОсновнойЦФО В (&ДоступныеЦФО)
)
Важно: Прямое использование UNION в самом тексте RLS может быть ограничено синтаксисом платформы. Однако использование ВЫБОР КОГДА иногда дает похожий эффект, заставляя СУБД вычислять условия последовательно.
Рассмотрим продвинутый метод для высоконагруженных систем. Начиная с версии платформы 8.3.17, мы можем передавать во временные таблицы параметров сеанса готовые списки разрешенных ссылок или ключей. Это позволяет избежать повторных вычислений прав в сложных иерархиях.
Проанализируем преимущества этого подхода:
Выясним, какой метод выбрать в конкретной ситуации. Если ваша база данных невелика, достаточно вынести список разрешенных значений в параметры сеанса (Метод 1). Для крупных систем с миллионами записей лучшим решением будет использование механизма Ключей доступа БСП или денормализация (Метод 2), так как это полностью убирает логическую неопределенность ИЛИ на уровне СУБД, гарантируя стабильную скорость работы индексов.