При работе в конфигурациях с включенным механизмом RLS (Row Level Security) программисты часто сталкиваются с ситуацией, когда результат запроса содержит записи со ссылками, отображаемыми как «Объект не найден». Это происходит в тех случаях, когда пользователю разрешено чтение основного объекта (например, документа), но запрещено чтение объекта, на который ссылается один из его реквизитов (например, справочник Контрагенты). В интерфейсе и в результатах запроса ВЫБРАТЬ РАЗРЕШЕННЫЕ такая ссылка превращается в «битую».
Рассмотрим подробно, как грамотно отсечь такие записи в запросе, не внося изменений в настройки прав доступа и саму конфигурацию, особенно если данных в базе очень много (миллионы записей).
Разберем ситуацию: в запросе используется ключевое слово РАЗРЕШЕННЫЕ. Платформа 1С автоматически модифицирует этот запрос, добавляя условия из настроек RLS. Если ограничение на документ настроено по «прочим полям» (например, по организации), то сам документ попадает в выборку. Однако, если на справочник, указанный в реквизите этого документа, у пользователя нет прав на чтение конкретной записи, платформа возвращает только идентификатор (GUID), но запрещает получение данных из таблицы справочника. В результате мы получаем ссылку, которая не может быть разрешена в объект.
Один из самых простых и быстрых способов — проверить наличие любого обязательного реквизита связанного объекта (например, Код или Наименование). Если у пользователя нет прав на чтение контрагента, то при попытке обратиться к его коду через точку, SQL-сервер вернет NULL.
Проанализируем пример кода, который позволяет быстро отфильтровать недоступные записи:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ПоступлениеИнформации.Организация КАК Организация,
ПоступлениеИнформации.Подразделение КАК Подразделение,
ПоступлениеИнформации.Контрагент КАК Контрагент
ИЗ
Документ.ПоступлениеИнформации КАК ПоступлениеИнформации
ГДЕ
НЕ ПоступлениеИнформации.Контрагент.Код ЕСТЬ NULL
Важный нюанс: при таком подходе будут отсечены и те документы, где поле Контрагент просто не заполнено (пустая ссылка). Если нам нужно оставить пустые ссылки, но убрать именно «запрещенные» объекты, условие нужно усложнить.
Рассмотрим более надежный с точки зрения производительности SQL вариант. Использование ВНУТРЕННЕГО СОЕДИНЕНИЯ с таблицей справочника, к которому применяется RLS, автоматически исключит из итоговой выборки те записи, на чтение которых у пользователя нет прав. Поскольку 1С применит ограничения и к таблице документа, и к таблице справочника, в результат попадут только пересечения доступных данных.
Выясним, как это выглядит в коде:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
Док.Организация КАК Организация,
Док.Подразделение КАК Подразделение,
Док.Контрагент КАК Контрагент
ИЗ
Документ.ПоступлениеИнформации КАК Док
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК К
ПО Док.Контрагент = К.Ссылка
Этот метод считается наиболее «чистым», так как оптимизатор СУБД эффективно обрабатывает соединения. Однако помните, что если в документе Контрагент не заполнен, такая строка также пропадет из результата.
Если задача требует сложной логики (например, нужно сохранить пустые ссылки, но убрать «Объект не найден»), можно использовать временные таблицы (ВТ). Этот метод был предложен в обсуждении как способ явно разделить выборку доступных контрагентов и выборку документов.
Разберем по шагам реализацию с учетом оптимизации для миллионов записей:
Ссылка.Пример реализации:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
Контрагенты.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_РазрешенныеКонтрагенты
ИЗ
Справочник.Контрагенты КАК Контрагенты
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
Док.Организация,
Док.Подразделение,
Док.Контрагент
ИЗ
Документ.ПоступлениеИнформации КАК Док
ГДЕ
(Док.Контрагент В (ВЫБРАТЬ ВТ.Ссылка ИЗ ВТ_РазрешенныеКонтрагенты КАК ВТ)
ИЛИ Док.Контрагент = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка))
Внимание: на больших объемах данных (миллионы записей) создание временной таблицы может занимать значительное время (в обсуждении упоминалось около 15 секунд). Если важна мгновенная реакция системы, лучше использовать Способ 1 или Способ 2.
Часто возникает вопрос: как не потерять пустые значения при фильтрации «битых» ссылок? Посмотрим на пример кода, который изящно решает эту задачу через ЛЕВОЕ СОЕДИНЕНИЕ и проверку на NULL:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
Док.Контрагент,
Док.Организация
ИЗ
Документ.ПоступлениеИнформации КАК Док
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Док.Контрагент = Контрагенты.Ссылка
ГДЕ
ВЫБОР
КОГДА Док.Контрагент = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
ТОГДА ИСТИНА
ИНАЧЕ НЕ Контрагенты.Ссылка ЕСТЬ NULL
КОНЕЦ
В данном случае, если контрагент в документе не пустой, но его нет в «разрешенной» таблице справочника (результат RLS), то Контрагенты.Ссылка будет NULL, и условие ИНАЧЕ отфильтрует такую строку. Пустые же ссылки пройдут проверку в первом условии КОГДА.
Проанализировав ситуацию с миллионами записей, можно сделать следующие выводы:
TempDB — это тяжелая операция. Если возможно решить задачу через прямое соединение или условие ГДЕ с обращением через точку, используйте их.РАЗРЕШЕННЫЕ проверяет право Чтение. Если данные всё равно не отображаются в интерфейсе, проверьте право Просмотр.Таким образом, мы рассмотрели основные способы борьбы с «невидимыми» объектами в запросах. Выбор конкретного метода зависит от требований к обработке пустых ссылок и объема данных в вашей базе.