При работе с высоконагруженными базами данных 1С часто возникает необходимость очистки огромных массивов информации. Рассмотрим классическую ситуацию: имеется самописная конфигурация и два связанных регистра сведений. В первом регистре содержится 8 000 000 записей (измерения: ссылка и дата). Во втором — 200 000 000 записей (есть ссылка, но нет даты). Задача состоит в том, чтобы удалить часть записей из второго регистра за определенный период, опираясь на данные первого. Удаление типовыми средствами 1С идет крайне медленно, поэтому проанализируем, как перенести эту операцию на уровень MS SQL Server, и разберем сопутствующие нюансы оптимизации.
Сначала разберем, как обычно выглядит процесс удаления большого количества записей через объектную модель 1С. Программист формирует массив ключей и в цикле очищает набор записей:
Для Каждого Ключ Из Ключи Цикл
Набор.Отбор.Ключ.Установить(Ключ);
Набор.Записать();
КонецЦикла;
Даже если распараллелить этот процесс с помощью фоновых заданий, производительность оставляет желать лучшего. Проблема кроется в нескольких факторах. Во-первых, объект НаборЗаписей поддерживает отборы только на равенство. На каждую уникальную комбинацию измерений формируется отдельный запрос к СУБД на чтение и отдельный на запись. Во-вторых, при записи срабатывают события ПередЗаписью и ПриЗаписи, могут вызываться подписки на события и производиться регистрация изменений для узлов обмена. Чтобы ускорить типовое удаление, необходимо программно устанавливать свойство ОбменДанными.Загрузка = Истина и объединять операции в транзакции по 5-10 тысяч записей. Но даже при этих оптимизациях для 150 миллионов строк процесс может занять сутки и более.
Посмотрим на пример прямого удаления через MS SQL. Идея состоит в том, чтобы использовать подзапрос с условием по дате. Однако, часто при написании подобных конструкций возникает ошибка синтаксиса. Рассмотрим пример такого неудачного запроса:
DELETE FROM [Test].[dbo].[_InfoRg1960]
WHERE [_Fld1961] IN (
SELECT [_Fld1948], [_Fld1951]
FROM [Test].[dbo].[_InfoRg1947]
WHERE [_Fld1948] < '4015-08-01'
)
Выполнение такого кода приведет к ошибке: "В списке выбора можно указать только одно выражение, если вложенный запрос не предварен EXISTS". Причина кроется в том, что оператор IN ожидает для сравнения один столбец, а во вложенном запросе указано два ([_Fld1948] и [_Fld1951]). Выясним причину и исправим ошибку, оставив в подзапросе только поле ключа:
DELETE FROM [Test].[dbo].[_InfoRg1960]
WHERE [_Fld1961] IN (
SELECT [_Fld1948]
FROM [Test].[dbo].[_InfoRg1947]
WHERE [_Fld1948] < '4015-08-01'
)
После этого исправления запрос отработает, и скорость удаления возрастет на порядки: то, что в 1С занимало 15–25 минут, на уровне SQL выполняется за 10–15 секунд.
Удаление 150 миллионов строк одним запросом DELETE вызовет колоссальный рост журнала транзакций (Transaction Log) и может привести к зависанию базы или исчерпанию места на диске. Рассмотрим методы, как сделать процесс безопасным.
TOP. Например, удаляйте по 100 000 строк в цикле. Это позволит контролировать размер транзакции. Пример синтаксиса: DELETE TOP (100000) FROM ... WHERE ...WAITFOR DELAY '00:00:02', чтобы давать серверу время на обработку других запросов.DBCC SHRINKFILE и верните полную модель восстановления (Full).Если из таблицы в 200 миллионов записей требуется удалить 150 миллионов, стандартный оператор DELETE может быть не лучшим выбором. Разберем альтернативные и значительно более быстрые подходы, которые не пишут каждую удаляемую строку в лог транзакций.
Метод 1: Перенос и очистка (SELECT INTO + TRUNCATE)
Вместо удаления лишнего, мы сохраняем нужное. Этот метод идеален для свертки баз:
SELECT * INTO TempTable FROM TargetTable WHERE ...TRUNCATE TABLE TargetTableINSERT INTO TargetTable SELECT * FROM TempTableМетод 2: Утилита BCP (Bulk Copy Program)
Аналог первого метода, но для случаев, когда временная таблица не помещается в оперативной памяти сервера. Оставляемые данные выгружаются быстрым потоком в текстовый файл на диск через утилиту командной строки bcp. Затем таблица очищается командой TRUNCATE, и данные заливаются обратно через BULK INSERT.
Проанализируем шаги, которые обязательно нужно предпринять после того, как лишние данные покинули базу, чтобы система 1С продолжила работать быстро и без сбоев.
UPDATE STATISTICS для очищенной таблицы.ALTER INDEX ALL ON [ИмяТаблицы] REBUILD. К слову, полезным трюком перед удалением является временное отключение некластерных индексов (Disable) с их последующим перестроением, что ускорит сам процесс очистки.Используя комбинированный подход — перенос логики отборов на уровень СУБД, управление транзакционным логом и методы массовой вставки/очистки (TRUNCATE), — вы сможете выполнять регламентные задачи по очистке данных безопасно и в сотни раз быстрее, чем это предлагают типовые объектные механизмы платформы 1С.