Как ускорить поиск и удаление неиспользуемых элементов справочников при больших объемах данных?

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

При работе с крупными базами данных 1С, где счет элементов в справочниках (например, ДоговорыКонтрагентов) идет на десятки и сотни тысяч, стандартный метод НайтиПоСсылкам() начинает работать крайне медленно. Это происходит из-за того, что платформа последовательно перебирает все таблицы метаданных, в которых теоретически может встретиться ссылка на данный объект. В этой статье мы разберем, как оптимизировать этот процесс, внедрим многопоточность и рассмотрим альтернативные методы поиска «мусорных» объектов.

Причина низкой производительности стандартных механизмов

Стандартный метод НайтиПоСсылкам() выполняет поиск по всем объектам метаданных, где тип реквизита совпадает с типом искомого объекта. Если в конфигурации сотни документов и регистров, поиск для каждого отдельного элемента превращается в тяжелую операцию. Ситуация усугубляется, если база работает на PostgreSQL, так как планы запросов при массовых объединениях (UNION ALL) могут быть неоптимальными. Выясним, как обойти эти ограничения.

Стратегия 1: Использование критериев отбора

Одним из самых быстрых способов проверки наличия ссылок является использование объекта конфигурации КритерииОтбора. Если в конфигурации уже есть критерий, например, ДокументыПоДоговоруКонтрагента, обращение к нему происходит значительно быстрее, чем общий поиск ссылок. Это связано с тем, что критерий отбора — это заранее описанный состав таблиц, по которым СУБД может эффективно искать данные.

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


ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1
    Критерий.Ссылка КАК Ссылка
ИЗ
    КритерийОтбора.ДокументыПоДоговоруКонтрагента(&ДоговорНаПроверку) КАК Критерий

Преимущество: такой запрос выполняется доли секунды даже в базах объемом в несколько сотен гигабайт. Если критерий возвращает хотя бы одну запись, договор точно используется, и его можно исключить из дальнейшей обработки.

Стратегия 2: Метод «От обратного» через временные таблицы

Вместо того чтобы проверять каждый договор по отдельности, проанализируем ситуацию глобально. Мы можем составить список всех используемых договоров и вычесть их из общего списка справочника. Это наиболее эффективный подход для массовой очистки.

Разберем алгоритм по шагам:

  1. Выбираем уникальные идентификаторы договоров из таблиц, где они встречаются чаще всего (например, РегистрБухгалтерии, РегистрНакопления.Взаиморасчеты, основные документы).
  2. Помещаем этот список во временную таблицу.
  3. Выполняем левое соединение справочника с этой таблицей и отбираем записи, где значение во временной таблице равно NULL.

Пример реализации такого запроса:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    Таблица.Договор КАК Договор
ПОМЕСТИТЬ ВТ_ИспользуемыеДоговоры
ИЗ
    (ВЫБРАТЬ ДоговорКонтрагента ИЗ Документ.РеализацияТоваровУслуг
     ОБЪЕДИНИТЬ
     ВЫБРАТЬ ДоговорКонтрагента ИЗ Документ.ПоступлениеТоваровУслуг
     ОБЪЕДИНИТЬ
     ВЫБРАТЬ Субконто1 ИЗ РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто 
     ГДЕ Субконто1 ССЫЛКА Справочник.ДоговорыКонтрагентов) КАК Таблица;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Договоры.Ссылка
ИЗ
    Справочник.ДоговорыКонтрагентов КАК Договоры
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИспользуемыеДоговоры КАК Используемые
    ПО Договоры.Ссылка = Используемые.Договор
ГДЕ
    Используемые.Договор ЕСТЬ NULL
    И НЕ Договоры.ПометкаУдаления

Такой подход позволяет за один проход отсеять 95-99% используемых объектов. Оставшиеся «подозрительные» элементы можно допроверить штатным методом НайтиПоСсылкам().

Стратегия 3: Многопоточность и распараллеливание

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

Разделение по ГУИД (GUID): Самый надежный способ разделить справочник на равные порции без изменения его структуры — использовать последний символ уникального идентификатора. ГУИДы распределены равномерно, что дает предсказуемую нагрузку на каждый поток.

Рассмотрим пример кода для запуска потоков:


КоличествоПотоков = 8;
Для НомерПотока = 1 По КоличествоПотоков Цикл
    ПараметрыЗадания = Новый Массив;
    ПараметрыЗадания.Добавить(НомерПотока);
    ПараметрыЗадания.Добавить(КоличествоПотоков);
    
    ФоновыеЗадания.Выполнить("МойМодуль.ПроверитьПорциюДоговоров", ПараметрыЗадания);
КонецЦикла;

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

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

Стратегия 4: Фильтрация «мусорных» ссылок

Часто объекты нельзя удалить, потому что на них ссылаются «бесполезные» записи: технические регистры сведений (например, версионирование объектов), непроведенные документы или служебные настройки. Рассмотрим, как игнорировать такие ссылки.

Метод НайтиПоСсылкам() принимает вторым параметром массив объектов метаданных, в которых нужно искать. Мы можем заранее составить «белый список» важных таблиц (Документы, Регистры бухгалтерии, Регистры накопления) и искать только в них.


ОбъектыДляПоиска = Новый Массив;
ОбъектыДляПоиска.Добавить(Метаданные.Документы.РеализацияТоваровУслуг);
ОбъектыДляПоиска.Добавить(Метаданные.РегистрыНакопления.ВзаиморасчетыСКонтрагентами);
// Добавляем только значимые метаданные

ТаблицаСсылок = НайтиПоСсылкам(МассивПроверяемыхОбъектов, ОбъектыДляПоиска);

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

Особенности работы с PostgreSQL

Поскольку ваша база работает на PostgreSQL, следует учитывать, что прямые SQL-запросы к таблицам 1С требуют понимания структуры хранения UUID. 1С хранит ссылки в формате bytea длиной 16 байт. Если вы решите писать прямой SQL-скрипт для поиска ссылок, помните, что идентификаторы в базе перевернуты (хранятся в Big-Endian или смешанном формате).

Проанализируем пример логики SQL для поиска ссылок в конкретной таблице:

SELECT _IDRRef FROM _ReferenceX WHERE _FldY_RRRef = '\x...'::bytea

Однако использование прямого SQL в 1С оправдано только в крайних случаях, так как структура таблиц может меняться при реструктуризации. Лучше использовать механизм External Data Sources (Внешние источники данных) или Query в 1С с предварительным отсевом по метаданным.

Итоги и рекомендации

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

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

← На главную