В процессе разработки и поддержки конфигураций на платформе 1С:Предприятие программисты регулярно сталкиваются с проблемой нарушения ссылочной целостности базы данных. На уровне интерфейса это проявляется в виде удручающих надписей «Объект не найден...», за которыми скрывается конкретный уникальный идентификатор (UUID) несуществующей записи. Подобные «битые» ссылки могут проникать в регистры сведений, регистры накопления или реквизиты других объектов в результате некорректного обмена данными, удаления объектов без контроля ссылочной целостности или сбоев в работе СУБД.
Когда нам требуется построить отчет или выполнить обработку данных, присутствие битых ссылок может исказить результаты или привести к ошибкам в логике программы. В этой статье мы подробно разберем, как эффективно выявлять и отсекать битые ссылки непосредственно в запросах 1С, как оптимизировать такие запросы во избежание «зависаний» системы, и какие архитектурные нюансы следует при этом учитывать.
Давайте проанализируем ситуацию на примере запроса из исходного сообщения. Разработчик пытается соединить справочник КлючиАналитикиУчетаНоменклатуры с регистром сведений АналитикаУчетаНоменклатуры и накладывает следующее условие фильтрации:
ГДЕ
ДанныеРегистра.КлючАналитики ЕСТЬ NULL
Выясним причину, почему этот фильтр не поможет избавиться от битых ссылок. Дело в том, что битая ссылка — это не NULL. В базе данных в этом поле физически записан корректный тип данных и вполне определенный 128-битный GUID. С точки зрения СУБД, в поле таблицы хранится значение. То, что в целевой таблице справочника больше нет строки с таким уникальным идентификатором, СУБД «не волнует» до тех пор, пока мы явно не попытаемся обратиться к данным этой целевой таблицы.
Поэтому простая проверка поля на NULL отберет только те записи, по которым вообще не произошло левого соединения (то есть записи отсутствуют в регистре), но пропустит все существующие записи регистра, содержащие битые ссылки.
Чтобы понять, существует ли объект в базе данных, нам необходимо заставить систему обратиться к физической таблице этого объекта. На форумах часто предлагают обращаться к какому-нибудь стандартному реквизиту, например, к коду или наименованию:
ГДЕ
НЕ СчетНаОплатуПокупателю.Организация.Код ЕСТЬ NULL
Этот способ рабочий, но далеко не самый оптимальный. Обращение к текстовым полям (наименование) или индексируемым полям (код) нагружает СУБД, заставляя ее выполнять лишнюю работу по чтению этих данных. Платформа 1С предоставляет более изящное решение.
У любого ссылочного типа в 1С есть скрытое системное свойство Ссылка, которое указывает на саму себя. Если объект реально существует в базе данных, то обращение к конструкции ИмяПоля.Ссылка вернет корректную ссылку. Если же ссылка является битой, то попытка разыменовать это поле и получить внутреннюю ссылку вернет значение NULL.
Рассмотрим, как будет выглядеть условие в запросе для исключения битых ссылок:
ГДЕ
НЕ ДанныеРегистра.КлючАналитики.Ссылка ЕСТЬ NULL
Этот запрос отработает максимально быстро, так как СУБД проверит исключительно факт наличия записи в индексной таблице по первичному ключу (UUID), не тратя ресурсы на чтение тяжелых прикладных данных.
Иногда при использовании конструкции проверки ссылки разработчики замечают, что запрос начинает выполняться критически медленно или вовсе «вешает» систему. Разберем по шагам, почему это происходит и как исправить ситуацию.
Проблема кроется в **неявных соединениях** (разыменовании полей) при работе с **составными типами данных**. Представим, что проверяемое поле (например, ДокументОснование или Регистратор) может содержать ссылки на 30 различных видов документов. Когда мы пишем:
ГДЕ
НЕ ДанныеРегистра.ДокументОснование.Ссылка ЕСТЬ NULL
Платформа 1С при трансляции этого кода в SQL-запрос вынуждена сгенерировать операторы LEFT OUTER JOIN абсолютно ко всем 30 таблицам документов, входящим в составной тип! СУБД пытается объединить десятки таблиц, что приводит к колоссальному падению производительности и зависанию.
Чтобы решить эту проблему, мы должны явно указать платформе, к какой именно таблице нужно обращаться, ограничив тип данных с помощью оператора ВЫРАЗИТЬ. Посмотрим на пример оптимизированного условия:
ГДЕ
НЕ ВЫРАЗИТЬ(ДанныеРегистра.КлючАналитики КАК Справочник.КлючиАналитикиУчетаНоменклатуры).Ссылка ЕСТЬ NULL
Использование оператора ВЫРАЗИТЬ указывает системе, что соединение нужно строить только с одной конкретной таблицей справочника КлючиАналитикиУчетаНоменклатуры. Все остальные типы будут проигнорированы, и SQL-запрос выполнится мгновенно.
Разработчику очень важно различать три принципиально разных состояния поля, чтобы случайно не отсечь валидные и нужные данные при проверках:
Справочник.Организации.ПустаяСсылка) — это предопределенное системное значение с нулевым GUID. Это НЕ битая ссылка, она реально существует в пространстве типов 1С, и метод Ссылка.Ссылка для нее НЕ вернет NULL.Если мы хотим отфильтровать данные и найти только истинно битые ссылки (исключив при этом пустые ссылки и значения Неопределено), нам следует использовать комплексное условие в запросе:
ГДЕ
ДанныеРегистра.КлючАналитики.Ссылка ЕСТЬ NULL
И ДанныеРегистра.КлючАналитики <> НЕОПРЕДЕЛЕНО
И ДанныеРегистра.КлючАналитики <> ЗНАЧЕНИЕ(Справочник.КлючиАналитикиУчетаНоменклатуры.ПустаяСсылка)
Такой подход гарантирует точечное обнаружение поврежденных данных без зацепа пустых и неопределенных значений.
Один из важнейших подводных камней при поиске битых ссылок — работа механизма **RLS (Row-Level Security)**, то есть ограничений прав доступа на уровне записей. Если у текущего пользователя нет прав на чтение определенных элементов справочника или документов, система ведет себя следующим образом:
При выполнении запроса СУБД, подчиняясь правилам RLS, не сможет прочитать защищенные строки из целевой таблицы. Из-за этого левое соединение вернет NULL для свойства Ссылка. Программа посчитает эту ссылку «битой» (ведь объект якобы не найден), хотя на самом деле объект существует, просто у пользователя недостаточно прав для его просмотра.
Решение проблемы RLS: Все регламентные процедуры, обработки поиска, очистки или исправления битых ссылок необходимо выполнять строго в **привилегированном режиме**. Для этого код вызова запроса должен быть перенесен в серверный модуль с установленным флагом Привилегированный или обернут в конструкцию:
УстановитьПривилегированныйРежим(Истина);
// Выполнение запроса по поиску битых ссылок
УстановитьПривилегированныйРежим(Ложь);
Это временно отключит проверки RLS на время выполнения кода и гарантирует защиту от ложных срабатываний.
Если вам требуется проверить ограниченное количество ссылок непосредственно в коде на встроенном языке (вне текста запроса), можно использовать альтернативные методы. Рассмотрим их подробнее.
Метод ПолучитьОбъект(). Самым простым и надежным способом является попытка получить объект по ссылке:
Если СсылкаНаОбъект.ПолучитьОбъект() = Неопределено Тогда
// Объект физически отсутствует в базе данных — ссылка битая
КонецЕсли;
Важное предупреждение: Метод ПолучитьОбъект() считывает абсолютно все реквизиты объекта и загружает их в оперативную память. Использовать данный метод в циклах при обработке больших массивов данных категорически запрещено из-за критического падения производительности.
Более легкий способ проверки в коде — обращение к системным свойствам в блоке попытки:
Попытка
ВерсияДанных = СсылкаНаОбъект.ВерсияДанных;
// Ссылка валидная
Исключение
// Ошибка чтения — ссылка битая или нет прав доступа
КонецПопытки;
Помните, что фильтрация битых ссылок в запросах — это лишь временная мера, «костыль», призванный скрыть симптомы проблемы. Правильным архитектурным решением является полное устранение поврежденных ссылок из базы данных.
Для этого в платформе 1С предусмотрен штатный механизм — Тестирование и исправление базы данных (ТИИ). Запустив ТИИ в режиме конфигуратора, вы можете настроить правила обработки нарушений ссылочной целостности:
Неопределено.Для специализированных регистров, таких как ключи аналитики учета номенклатуры (которые обсуждались в исходном сообщении форума), разработчики типовых конфигураций (ERP, КА, УТ) также предоставляют специализированные обработки исправления и переформирования ключей аналитики. Рекомендуется использовать именно их для поддержания порядка в вашей информационной базе.