Когда вы сталкиваетесь с ситуацией, где в справочнике 1С появилось огромное количество элементов (например, 600 000 групп), и попытка их удаления, пометки на удаление или даже простого отбора приводит к зависаниям и аварийному завершению работы программы, это сигнализирует о серьезной проблеме с базой данных. В таких случаях стандартные методы могут быть неэффективны.
В этой статье мы подробно разберем, как подойти к решению подобной задачи, рассмотрим методы удаления больших объемов данных как средствами платформы 1С, так и с помощью прямого вмешательства в СУБД (SQL).
Прежде чем приступать к удалению, критически важно выяснить истинную причину зависаний и аномального роста справочника. В рассматриваемом случае, пользователь столкнулся не с классическим "зацикливанием", когда группа ссылается сама на себя как на родителя, а с массовым созданием дубликатов групп с одинаковыми наименованиями. Эти дубликаты были вложены друг в друга или просто образовали огромный объем данных, который платформа 1С не могла эффективно обработать при попытке интерактивного или программного удаления.
Подобные проблемы часто возникают из-за:
Мы наблюдаем ситуацию, когда попытка интерактивного открытия или отбора элементов справочника приводит к зависанию и аварийному завершению работы 1С. Это происходит потому, что платформа пытается выполнить проверки ссылочной целостности, построить иерархию, обработать большой объем данных, что приводит к нехватке оперативной памяти или таймаутам на стороне СУБД.
Один из самых безопасных подходов, если проблема не настолько критична, чтобы требовать прямого вмешательства в СУБД, — это удаление данных порциями (пакетное удаление). Вместо того чтобы пытаться удалить все 600 000 элементов за один раз, мы будем обрабатывать их небольшими пакетами, что позволит избежать перегрузки системы.
Разберем процесс по шагам:
ОбменДанными.Загрузка = Истина: Это свойство объекта 1С временно отключает часть стандартной бизнес-логики, проверок и триггеров, которые выполняются при записи или удалении. Это может существенно ускорить процесс и помочь обойти зависания, связанные с излишними проверками. Однако, используйте его с осторожностью, поскольку оно может нарушить целостность данных, если не учитывать все зависимости. Не забудьте сбросить флаг в Ложь после завершения операций, если объект будет использоваться дальше.Посмотрим на пример программного кода для пакетной пометки на удаление:
// Пример программного кода для пометки на удаление порциями
// Предполагаем, что у нас есть справочник с именем "Номенклатура"
// и мы хотим удалить группы с определенным наименованием, например, "Полуфабрикаты"
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ИСТИНА
| И Номенклатура.Наименование = &НаименованиеГруппыДляУдаления
|УПОРЯДОЧИТЬ ПО
| Номенклатура.Код
|ИТОГИ ПО
| Номенклатура.Ссылка ИЕРАРХИЯ"; // Позволит обрабатывать иерархически, если нужно
Запрос.УстановитьПараметр("НаименованиеГруппыДляУдаления", "Полуфабрикаты"); // Укажите нужное наименование группы
Выборка = Запрос.Выполнить().Выбрать();
СчетчикОбработанныхОбъектов = 0;
ПорогПакетногоУдаления = 5000; // Определяем размер пачки для обработки
НачатьТранзакцию(); // Рекомендуется использовать транзакции для пакетной обработки
Пока Выборка.Следующий() Цикл
Попытка
ОбъектСправочника = Выборка.Ссылка.ПолучитьОбъект();
// Если объект успешно получен, приступаем к его обработке
Если ОбъектСправочника <> Неопределено Тогда
ОбъектСправочника.ОбменДанными.Загрузка = Истина; // Отключаем часть проверок
// Если необходимо сначала "расциклить" или сбросить родителя
// (актуально, если группа была родителем сама себе или имела некорректного родителя)
// ОбъектСправочника.Родитель = Справочники.Номенклатура.ПустаяСсылка();
ОбъектСправочника.ПометкаУдаления = Истина; // Помечаем на удаление
ОбъектСправочника.Записать(); // Записываем изменения
ОбъектСправочника.ОбменДанными.Загрузка = Ложь; // Возвращаем флаг в исходное состояние
СчетчикОбработанныхОбъектов = СчетчикОбработанныхОбъектов + 1;
Если СчетчикОбработанныхОбъектов % ПорогПакетногоУдаления = 0 Тогда
Сообщить("Помечено на удаление " + СчетчикОбработанныхОбъектов + " объектов. Фиксируем транзакцию...");
ЗафиксироватьТранзакцию(); // Фиксируем изменения пакета
НачатьТранзакцию(); // Начинаем новую транзакцию для следующего пакета
ОбработкаПрерыванияПользователя(); // Позволяет прервать выполнение
КонецЕсли;
КонецЕсли;
Исключение
Сообщить("Ошибка при обработке объекта: " + Выборка.Ссылка + ". " + ОписаниеОшибки());
Продолжить; // Продолжаем обработку, игнорируя текущий проблемный объект
КонецПопытки;
КонецЦикла;
// Фиксируем оставшиеся изменения
ЗафиксироватьТранзакцию();
Сообщить("Всего помечено на удаление: " + СчетчикОбработанныхОбъектов + " объектов.");
// После пометки, необходимо запустить стандартную обработку "Удаление помеченных объектов"
// (Администрирование -> Обслуживание -> Удаление помеченных объектов) для окончательного удаления.
Помимо собственной разработки, мы также можем воспользоваться типовыми обработками 1С:
В случаях, когда средства платформы 1С не справляются (например, при экстремально больших объемах данных, глубоко вложенной иерархии или специфических проблемах, вызывающих зависания даже при пакетной обработке), прямое вмешательство на уровне СУБД становится единственным эффективным решением. Однако, этот подход требует высокой квалификации и чрезвычайной осторожности, так как неправильный запрос может привести к фатальной потере данных или нарушению целостности базы.
_Reference, за которым следует числовой идентификатор, например, _Reference32 или _Reference666. Если справочник находится в расширении, имя таблицы может иметь суффикс, например, _Reference666X. Мы можем найти это имя через конфигуратор 1С (меню "Сервис" -> "Параметры" -> "Администрирование" -> "Таблицы БД") или через SQL Management Studio, анализируя структуру базы.Давайте рассмотрим несколько сценариев использования SQL-запросов:
Если группы действительно "зациклены" (то есть, группа является родителем самой себя), или имеют некорректные ссылки на родителя, мы можем "расциклить" их, обнулив поле родителя. Это сделает такие группы корневыми, после чего их можно будет переместить или удалить средствами 1С.
В таблицах 1С в SQL:
_ParentIDRRef хранит ссылку на родителя. Пустая ссылка (корневой элемент) имеет значение CAST(0x00000000000000000000000000000000 AS varbinary(16)) или CAST(0 AS varbinary(16))._IDRRef хранит уникальный идентификатор элемента._Folder указывает тип элемента: 0x00 для группы, 0x01 для элемента.Пример SQL-запроса для обнуления родителя у групп, которые ссылаются сами на себя:
USE [ИмяВашейБазыДанных] -- Замените на имя вашей базы данных
UPDATE [_Reference32] -- Замените на имя таблицы вашего справочника
SET _ParentIDRRef = CAST(0x00000000000000000000000000000000 AS varbinary(16))
WHERE _ParentIDRRef = _IDRRef
AND _Folder = 0x00; -- Применяем только к группам
Этот запрос найдет все группы, у которых поле родителя указывает на их собственный идентификатор, и установит для них пустого родителя, эффективно "расцикливая" иерархию.
В случае, описанном в теме, когда имеется огромное количество дублирующихся групп по наименованию, и требуется удалить именно эти избыточные группы, сохраняя при этом элементы, которые могут быть привязаны к одной из таких групп, мы можем использовать более сложный SQL-запрос. Основная задача здесь — не удалить группу, если она является родителем для какого-либо элемента.
Рабочий SQL-запрос, который помог решить проблему в обсуждаемой теме:
USE [ИмяВашейБазыДанных] -- ***ОБЯЗАТЕЛЬНО замените на имя вашей базы данных***
-- Шаг 1: Удаляем дублирующиеся группы, которые не являются родителями для элементов
DELETE
FROM [_Reference666] -- ***ОБЯЗАТЕЛЬНО замените на имя таблицы вашего справочника***
WHERE [_Folder] = 0x00 -- Условие: удаляем только группы
AND [_Description] = 'НаименованиеПроблемнойГруппы' -- ***ОБЯЗАТЕЛЬНО замените на точное наименование удаляемых групп***
AND [_IDRRef] NOT IN (
SELECT [_ParentIDRRef]
FROM [_Reference666] -- ***ОБЯЗАТЕЛЬНО замените на имя таблицы вашего справочника***
WHERE [_Folder] = 0x01 -- Условие: выбираем идентификаторы групп, которые являются родителями для элементов
);
GO -- Разделитель пакетов, если выполняете в SQL Management Studio
-- Шаг 2: У оставшихся групп с таким же наименованием (их будет теперь меньше или одна)
-- очищаем поле родителя, чтобы они поднялись на корневой уровень.
-- Это поможет избежать проблем с неверными ссылками на удаленные родительские группы
-- и позволит корректно перестроить иерархию позже.
UPDATE [_Reference666] -- ***ОБЯЗАТЕЛЬНО замените на имя таблицы вашего справочника***
SET [_ParentIDRRef] = CAST(0 AS varbinary(16)) -- Устанавливаем пустого родителя
WHERE [_Folder] = 0x00 -- Условие: применяем только к группам
AND [_Description] = 'НаименованиеПроблемнойГруппы'; -- ***ОБЯЗАТЕЛЬНО замените на точное наименование групп, чьих родителей надо очистить***
Разберем логику запроса:
DELETE): Мы удаляем записи из таблицы справочника, где _Folder = 0x00 (это группа), _Description соответствует наименованию проблемной группы. Ключевая часть – AND [_IDRRef] NOT IN (...). Она гарантирует, что мы не удалим те группы, идентификаторы которых (_IDRRef) присутствуют в списке _ParentIDRRef для элементов (_Folder = 0x01). Таким образом, группы, к которым привязаны реальные элементы, останутся нетронутыми.UPDATE): После удаления избыточных групп, у тех групп, что остались с тем же наименованием, мы обнуляем поле родителя (_ParentIDRRef = CAST(0 AS varbinary(16))). Это необходимо, чтобы избежать появления "битых" ссылок на удаленные родительские группы и поднять эти группы на корневой уровень справочника. После этого пользователь или программист может вручную перераспределить их в правильную иерархию.Важное примечание: Как было выяснено в обсуждаемой теме, критически важно правильно указать имя таблицы справочника (например, _Reference666 или _Reference666X, если справочник из расширения). Неверное имя приведет к тому, что запрос не найдет записи и покажет "обработано 0 записей".
Если проблемные элементы составляют подавляющее большинство, и старых, "хороших" данных очень мало, или их легко вычленить, можно применить более радикальный подход:
TRUNCATE TABLE [ИмяТаблицыСправочника]; в SQL Management Studio. Эта команда очень быстро и без логирования удаляет все записи из таблицы. Это необратимая операция!ОбменДанными.Загрузка = Истина для ускорения процесса и обхода проверок.Этот метод наиболее быстр, но требует тщательного планирования выгрузки и уверенности в том, что все нужные данные будут сохранены и корректно загружены.
После проведения операций по удалению большого объема данных, мы рекомендуем выполнить следующие действия:
Удаление большого количества элементов из справочника 1С, особенно в условиях "зацикливания" или массового дублирования, является сложной задачей, требующей внимательного подхода. Мы рассмотрели два основных пути решения: поэтапное удаление средствами платформы 1С и прямое вмешательство через SQL-запросы. Выбор метода зависит от масштаба проблемы, ресурсов и квалификации специалиста. Всегда помните о резервном копировании и тестировании на копии базы данных!