В процессе эксплуатации крупных информационных баз 1С возникают ситуации, когда объем табличных частей определенных документов начинает занимать сотни гигабайт. Особенно это актуально для копий баз, предназначенных для разработки и тестирования, где полные исторические данные в строках документов не требуются, но важно сохранить сами документы и их движения (регистры). Рассмотрим, как эффективно решить задачу очистки табличных частей напрямую через СУБД MS SQL Server, сохраняя при этом целостность данных и производительность системы.
Прежде чем приступать к удалению, проанализируем, как 1С хранит данные документов. Документ в SQL обычно разделен на несколько таблиц:
_Document13383. В ней хранятся общие реквизиты: дата, номер, ссылка, проведен, пометка удаления и другие._Document13383_VT13393. Здесь хранятся строки документа.Связь между ними осуществляется через поле ссылки. В основной таблице это _IDRRef, а в таблице ТЧ — поле с именем _Document13383_IDRRef (имя может варьироваться в зависимости от конфигурации, но всегда содержит GUID родительского документа).
Если нам нужно удалить строки ТЧ только за определенный период (например, до 1 мая 2022 года), мы не можем просто применить фильтр к таблице ТЧ, так как в ней нет поля «Дата». Нам необходимо «соединиться» с основной таблицей документа. Проанализируем пример рабочего запроса:
DELETE vt
FROM _Document13383_VT13393 vt
INNER JOIN _Document13383 t ON vt._Document13383_IDRRef = t._IDRRef
WHERE t._Date_Time <= '40220501'
Разберем, что происходит в этом запросе:
DELETE vt — указываем СУБД, что удаление производится из псевдонима vt (наша табличная часть).INNER JOIN — связывает строки ТЧ с их «родителями» в основной таблице._Date_Time — поле даты. Обратите внимание на формат '40220501'. В 1С к году по умолчанию добавляется смещение 2000. Таким образом, 2022 год превращается в 4022.Важно: После такого масштабного удаления рекомендуется выполнить сжатие базы данных и обновление статистики, чтобы СУБД перестроила планы запросов:
DBCC SHRINKDATABASE ('Имя_Вашей_Базы', 10);
Иногда удобнее написать универсальную обработку, которая сама определит имена таблиц через метаданные и выполнит запрос. Выясним, как это реализовать, используя метод ПолучитьСтруктуруХраненияБазыДанных.
Процедура УдалитьТЧДокументовЧерезSQL(ИмяДокумента, ДатаКонца)
ОбъектМетаданных = Метаданные.Документы[ИмяДокумента];
МассивМД = Новый Массив;
МассивМД.Добавить(ОбъектМетаданных);
ТаблицаБД = ПолучитьСтруктуруХраненияБазыДанных(МассивМД, Истина);
// Получаем имя основной таблицы
ИмяТаблицыШапки = ТаблицаБД.Найти("Основная", "Назначение").ИмяТаблицыХранения;
Для каждого СтрокаТЗ Из ТаблицаБД Цикл
Если СтрокаТЗ.Назначение = "ТабличнаяЧасть" Тогда
ИмяТаблицыТЧ = СтрокаТЗ.ИмяТаблицыХранения;
ИмяПоляСсылки = "";
// Ищем поле связи с шапкой
Для каждого СтрокаПолей Из СтрокаТЗ.Поля Цикл
Если Найти(СтрокаПолей.ИмяПоляХранения, "_IDRRef") > 0 Тогда
ИмяПоляСсылки = СтрокаПолей.ИмяПоляХранения;
Прервать;
КонецЕсли;
КонецЦикла;
// Формируем SQL команду
ТекстКоманды = "DELETE vt FROM " + ИмяТаблицыТЧ + " vt
|INNER JOIN " + ИмяТаблицыШапки + " t ON vt." + ИмяПоляСсылки + " = t._IDRRef
|WHERE t._Date_Time <= '" + Формат(ДобавитьМесяц(ДатаКонца, 24000), "ДФ=yyyyMMdd") + "'";
// Здесь должен быть вызов выполнения команды на уровне SQL
ВыполнитьКомандуНаСервереSQL(ТекстКоманды);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Проанализируем ситуацию: если в табличной части 180 миллионов строк, выполнение одного огромного запроса DELETE может привести к следующим проблемам:
Рекомендация: Удаляйте данные порциями (батчами). Рассмотрим пример цикла для SQL:
WHILE 1 = 1
BEGIN
DELETE TOP (50000) vt
FROM _Document13383_VT13393 vt
INNER JOIN _Document13383 t ON vt._Document13383_IDRRef = t._IDRRef
WHERE t._Date_Time <= '40220501'
IF @@ROWCOUNT = 0 BREAK
END
Такой подход позволит транзакционному логу очищаться (если база в модели Simple) и снизит нагрузку на сервер.
Посмотрим на последствия прямого вмешательства в SQL, о которых часто забывают:
rphost или выполнить очистку серверного кэша.ALTER INDEX REBUILD для соответствующих таблиц. Это критически важно для сохранения скорости работы базы._KeyField: В современных версиях платформы (8.3) в ТЧ присутствует поле ключа строки. При обычном удалении строк через SQL проблем обычно не возникает, но если вы планируете не удалять, а подменять данные, помните о необходимости соблюдения уникальности этого поля.Если объем данных позволяет, всегда лучше использовать штатные механизмы платформы 1С. Это гарантирует очистку кэша и корректную работу индексов:
Объект = СсылкаНаДокумент.ПолучитьОбъект();
Объект.Товары.Очистить(); // Очищаем нужную ТЧ
Объект.ОбменДанными.Загрузка = Истина; // Отключаем проверки и регистрации
Объект.Записать();
Использование ОбменДанными.Загрузка = Истина значительно ускоряет процесс, так как платформа игнорирует обработчики событий и не выполняет лишних проверок. Однако для 180 миллионов строк этот метод всё равно будет медленнее, чем прямой SQL-запрос.
Выясним, какой метод выбрать: для разовой очистки огромной копии базы под разработку идеально подходит SQL DELETE с INNER JOIN порциями. Для регулярных регламентных операций в рабочей базе лучше использовать программный метод 1С.