Как убрать битые ссылки из запроса 1С

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

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

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

Постановка проблемы: почему простое сравнение с NULL не работает?

Давайте проанализируем ситуацию на примере запроса из исходного сообщения. Разработчик пытается соединить справочник КлючиАналитикиУчетаНоменклатуры с регистром сведений АналитикаУчетаНоменклатуры и накладывает следующее условие фильтрации:


ГДЕ
    ДанныеРегистра.КлючАналитики ЕСТЬ NULL

Выясним причину, почему этот фильтр не поможет избавиться от битых ссылок. Дело в том, что битая ссылка — это не NULL. В базе данных в этом поле физически записан корректный тип данных и вполне определенный 128-битный GUID. С точки зрения СУБД, в поле таблицы хранится значение. То, что в целевой таблице справочника больше нет строки с таким уникальным идентификатором, СУБД «не волнует» до тех пор, пока мы явно не попытаемся обратиться к данным этой целевой таблицы.

Поэтому простая проверка поля на NULL отберет только те записи, по которым вообще не произошло левого соединения (то есть записи отсутствуют в регистре), но пропустит все существующие записи регистра, содержащие битые ссылки.

Способ 1. Использование механизма проверки Ссылка.Ссылка (Самый быстрый метод)

Чтобы понять, существует ли объект в базе данных, нам необходимо заставить систему обратиться к физической таблице этого объекта. На форумах часто предлагают обращаться к какому-нибудь стандартному реквизиту, например, к коду или наименованию:


ГДЕ
    НЕ СчетНаОплатуПокупателю.Организация.Код ЕСТЬ NULL

Этот способ рабочий, но далеко не самый оптимальный. Обращение к текстовым полям (наименование) или индексируемым полям (код) нагружает СУБД, заставляя ее выполнять лишнюю работу по чтению этих данных. Платформа 1С предоставляет более изящное решение.

У любого ссылочного типа в 1С есть скрытое системное свойство Ссылка, которое указывает на саму себя. Если объект реально существует в базе данных, то обращение к конструкции ИмяПоля.Ссылка вернет корректную ссылку. Если же ссылка является битой, то попытка разыменовать это поле и получить внутреннюю ссылку вернет значение NULL.

Рассмотрим, как будет выглядеть условие в запросе для исключения битых ссылок:


ГДЕ
    НЕ ДанныеРегистра.КлючАналитики.Ссылка ЕСТЬ NULL

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

Способ 2. Оптимизация составных типов данных (Борьба с зависанием запросов)

Иногда при использовании конструкции проверки ссылки разработчики замечают, что запрос начинает выполняться критически медленно или вовсе «вешает» систему. Разберем по шагам, почему это происходит и как исправить ситуацию.

Проблема кроется в **неявных соединениях** (разыменовании полей) при работе с **составными типами данных**. Представим, что проверяемое поле (например, ДокументОснование или Регистратор) может содержать ссылки на 30 различных видов документов. Когда мы пишем:


ГДЕ
    НЕ ДанныеРегистра.ДокументОснование.Ссылка ЕСТЬ NULL

Платформа 1С при трансляции этого кода в SQL-запрос вынуждена сгенерировать операторы LEFT OUTER JOIN абсолютно ко всем 30 таблицам документов, входящим в составной тип! СУБД пытается объединить десятки таблиц, что приводит к колоссальному падению производительности и зависанию.

Чтобы решить эту проблему, мы должны явно указать платформе, к какой именно таблице нужно обращаться, ограничив тип данных с помощью оператора ВЫРАЗИТЬ. Посмотрим на пример оптимизированного условия:


ГДЕ
    НЕ ВЫРАЗИТЬ(ДанныеРегистра.КлючАналитики КАК Справочник.КлючиАналитикиУчетаНоменклатуры).Ссылка ЕСТЬ NULL

Использование оператора ВЫРАЗИТЬ указывает системе, что соединение нужно строить только с одной конкретной таблицей справочника КлючиАналитикиУчетаНоменклатуры. Все остальные типы будут проигнорированы, и SQL-запрос выполнится мгновенно.

Способ 3. Отличие битой ссылки от «Пустой ссылки» и «Неопределено»

Разработчику очень важно различать три принципиально разных состояния поля, чтобы случайно не отсечь валидные и нужные данные при проверках:

  1. Неопределено — встречается в полях составного типа, когда значение вообще не задано.
  2. Пустая ссылка (например, Справочник.Организации.ПустаяСсылка) — это предопределенное системное значение с нулевым GUID. Это НЕ битая ссылка, она реально существует в пространстве типов 1С, и метод Ссылка.Ссылка для нее НЕ вернет NULL.
  3. Битая ссылка — это заполненное поле с конкретным ненулевым GUID, записи под которым в соответствующей таблице СУБД нет.

Если мы хотим отфильтровать данные и найти только истинно битые ссылки (исключив при этом пустые ссылки и значения Неопределено), нам следует использовать комплексное условие в запросе:


ГДЕ
    ДанныеРегистра.КлючАналитики.Ссылка ЕСТЬ NULL
    И ДанныеРегистра.КлючАналитики <> НЕОПРЕДЕЛЕНО
    И ДанныеРегистра.КлючАналитики <> ЗНАЧЕНИЕ(Справочник.КлючиАналитикиУчетаНоменклатуры.ПустаяСсылка)

Такой подход гарантирует точечное обнаружение поврежденных данных без зацепа пустых и неопределенных значений.

Способ 4. Учет влияния прав доступа (RLS)

Один из важнейших подводных камней при поиске битых ссылок — работа механизма **RLS (Row-Level Security)**, то есть ограничений прав доступа на уровне записей. Если у текущего пользователя нет прав на чтение определенных элементов справочника или документов, система ведет себя следующим образом:

При выполнении запроса СУБД, подчиняясь правилам RLS, не сможет прочитать защищенные строки из целевой таблицы. Из-за этого левое соединение вернет NULL для свойства Ссылка. Программа посчитает эту ссылку «битой» (ведь объект якобы не найден), хотя на самом деле объект существует, просто у пользователя недостаточно прав для его просмотра.

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


УстановитьПривилегированныйРежим(Истина);
// Выполнение запроса по поиску битых ссылок
УстановитьПривилегированныйРежим(Ложь);

Это временно отключит проверки RLS на время выполнения кода и гарантирует защиту от ложных срабатываний.

Способ 5. Программная проверка ссылок в коде

Если вам требуется проверить ограниченное количество ссылок непосредственно в коде на встроенном языке (вне текста запроса), можно использовать альтернативные методы. Рассмотрим их подробнее.

Метод ПолучитьОбъект(). Самым простым и надежным способом является попытка получить объект по ссылке:


Если СсылкаНаОбъект.ПолучитьОбъект() = Неопределено Тогда
    // Объект физически отсутствует в базе данных — ссылка битая
КонецЕсли;

Важное предупреждение: Метод ПолучитьОбъект() считывает абсолютно все реквизиты объекта и загружает их в оперативную память. Использовать данный метод в циклах при обработке больших массивов данных категорически запрещено из-за критического падения производительности.

Более легкий способ проверки в коде — обращение к системным свойствам в блоке попытки:


Попытка
    ВерсияДанных = СсылкаНаОбъект.ВерсияДанных;
    // Ссылка валидная
Исключение
    // Ошибка чтения — ссылка битая или нет прав доступа
КонецПопытки;

Архитектурный итог: лечим причину, а не симптомы

Помните, что фильтрация битых ссылок в запросах — это лишь временная мера, «костыль», призванный скрыть симптомы проблемы. Правильным архитектурным решением является полное устранение поврежденных ссылок из базы данных.

Для этого в платформе 1С предусмотрен штатный механизм — Тестирование и исправление базы данных (ТИИ). Запустив ТИИ в режиме конфигуратора, вы можете настроить правила обработки нарушений ссылочной целостности:

Для специализированных регистров, таких как ключи аналитики учета номенклатуры (которые обсуждались в исходном сообщении форума), разработчики типовых конфигураций (ERP, КА, УТ) также предоставляют специализированные обработки исправления и переформирования ключей аналитики. Рекомендуется использовать именно их для поддержания порядка в вашей информационной базе.

← На главную