При работе с крупными базами данных, такими как 1С:Розница 2.3, часто возникает необходимость очистки справочника номенклатуры от «мусорных» записей. Основная сложность заключается в том, что стандартные механизмы контроля ссылочной целостности не позволяют удалить объект, если на него есть хотя бы одна ссылка. В типовых конфигурациях практически каждый элемент номенклатуры автоматически обрастает техническими записями в регистрах сведений (например, штрихкоды, коды SKU), что делает стандартное удаление невозможным без предварительной подготовки. Разберем, как автоматизировать этот процесс, не дожидаясь окончания работы стандартных функций часами.
Рассмотрим ситуацию: у нас есть около 4000 объектов номенклатуры, которые нужно проверить на наличие «реальных» ссылок (в документах поступления, реализации или остатках). Использование стандартного метода НайтиПоСсылкам в лоб для такого количества объектов приводит к катастрофическому падению производительности. Выясним причину: этот метод выполняет поиск по абсолютно всем таблицам базы данных, которые могут содержать ссылки на данный тип объекта. Он не останавливается, когда находит первую ссылку, а продолжает собирать полную статистику вхождений, что в нашем случае избыточно.
Если нам известно, что записи в регистре сведений Коды товаров SKU не представляют ценности при удалении (их можно очистить программно), мы можем исключить этот регистр из области поиска. Рассмотрим программный код, который позволяет сформировать массив исключений. Проанализируем, как можно динамически собрать список всех метаданных и исключить только нужный нам регистр:
СписИмен = Новый СписокЗначений;
СписИмен.Добавить("Документы");
СписИмен.Добавить("Справочники");
СписИмен.Добавить("РегистрыСведений");
Исключить = "РегистрСведений.КодыТоваровSKU"; // Уточните имя в вашей конфигурации
МассивИсключений = Новый Массив;
Для Каждого Мет из СписИмен Цикл
ИмяКоллекции = Мет.Значение;
Для Каждого ВидМета из Метаданные[ИмяКоллекции] Цикл
Если ВидМета.ПолноеИмя() = Исключить Тогда
Продолжить;
Иначе
// В некоторых случаях полезно добавить проверку на наличие реквизита типа Номенклатура
КонецЕсли;
КонецЦикла;
КонецЦикла;
// Теперь вызываем поиск, передав массив исключений четвертым параметром
ТаблицаСсылок = НайтиПоСсылкам(МассивНоменклатуры, , , МассивИсключений);
Этот способ значительно сократит объем обрабатываемых данных, однако функция всё равно будет просматривать все остальные таблицы до самого конца. Для 4000 позиций это может занять очень много времени.
Посмотрим на задачу под другим углом. Объект номенклатуры имеет информационную ценность только в том случае, если по нему были реальные движения. Проанализируем состояние склада. Если по позиции нет остатков в регистре накопления ТоварыНаСкладах и отсутствуют записи в регистрах продаж или закупок за последний год, такая номенклатура, скорее всего, является кандидатом на удаление, даже если на нее ссылаются цены или сегменты.
Вместо поиска ссылок по всей базе, мы можем сначала выполнить быстрый запрос к регистрам накопления. Если движений нет, мы переходим к следующему этапу проверки ссылок, что отсеет значительную часть «активной» номенклатуры еще на старте.
Для достижения максимальной скорости работы, особенно когда объектов тысячи, мы рекомендуем отказаться от НайтиПоСсылкам в пользу прямого обхода метаданных через запросы. Главный секрет оптимизации здесь — принцип первого вхождения. Нам не нужно знать, сколько раз номенклатура упоминается в документах; нам достаточно узнать, что она упоминается хотя бы один раз.
Разберем алгоритм по шагам:
СправочникСсылка.Номенклатура.ВЫБРАТЬ ПЕРВЫЕ 1.Прервать) и помечаем ее как «неудаляемую».Посмотрим на пример реализации такого поиска в упрощенном виде:
Для Каждого ТекНоменклатура Из СписокДляПроверки Цикл
ЕстьСсылки = Ложь;
Для Каждого ТаблицаМетаданных Из СписокТаблицДляПоиска Цикл
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1 Ссылка ИЗ " + ТаблицаМетаданных + " ГДЕ Номенклатура = &Номенклатура";
Запрос.УстановитьПараметр("Номенклатура", ТекНоменклатура);
Если НЕ Запрос.Выполнить().Пустой() Тогда
ЕстьСсылки = Истина;
Прервать; // Ссылка найдена, дальше искать по этой номенклатуре нет смысла
КонецЕсли;
КонецЦикла;
Если НЕ ЕстьСсылки Тогда
// Здесь можно программно удалить записи из Коды SKU и пометить на удаление саму номенклатуру
КонецЕсли;
КонецЦикла;
Проанализируем специфику регистра КодыТоваровSKU. В конфигурации «Розница» этот регистр часто используется для связи с торговым оборудованием и внешними системами (например, Эвотор). Важный момент: перед тем как окончательно удалять номенклатуру, записи в этом регистре нужно очистить. Сделать это лучше всего через набор записей с установленным отбором по номенклатуре:
Набор = РегистрыСведений.КодыТоваровSKU.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(ОбъектНоменклатуры);
Набор.Записать(); // Записываем пустой набор, тем самым удаляя связи
Только после этого объект можно будет удалить физически или пометить на удаление без возникновения ошибок целостности. Также стоит учитывать планы обмена. Если ваша номенклатура зарегистрирована к обмену с другими базами, удаление ссылки может привести к ошибкам в узлах обмена. Перед массовой очисткой убедитесь, что регистрация изменений по этим объектам также удалена.
Подводя итог, отметим, что для 4000 объектов наиболее эффективным будет комбинированный метод: сначала отсеиваем номенклатуру с остатками через быстрый запрос, затем проводим оптимизированный ручной поиск ссылок с прерыванием цикла, исключая технические регистры.