При обслуживании и оптимизации баз данных на платформе 1С:Предприятие 7.7 нередко возникает необходимость пакетной очистки устаревших данных — например, когда требуется убрать из регистров движения и остатки по номенклатуре, помеченной на удаление. Выполнение таких операций стандартными средствами платформы может занимать часы или дни, поэтому разработчики прибегают к прямым SQL-запросам через внешнюю компоненту 1С++. Однако прямое вмешательство в таблицы СУБД требует точного понимания структуры таблиц регистров и синтаксиса T-SQL, чтобы не разрушить ссылочную и логическую целостность информационной базы.
Разберем по шагам, как устроено хранение регистров в 1С 7.7, как предварительно протестировать выборку и как корректно выполнить удаление записей.
В архитектуре 1С 7.7 каждый регистр остатков физически делится на две независимые таблицы на уровне Microsoft SQL Server:
RAxxxx, где xxxx — идентификатор регистра). Содержит каждую отдельную запись, порожденную проведением документов: позицию документа, знак движения (+/-) и значения измерений и ресурсов. В метапарсере 1С++ к ней обращаются через конструкцию $Регистр.ИмяРегистра.RGxxxx). Содержит рассчитанные срезы остатков на начало каждого расчетного периода (месяца), зафиксированные в поле PERIOD. В метапарсере 1С++ к этой таблице обращаются как $РегистрИтоги.ИмяРегистра.Справочник номенклатуры хранится в таблице с префиксом SCxxxx (метаимя $Справочник.Номенклатура), а признак пометки на удаление определяется полем ISMARK (где значение 1 означает установленную пометку).
Любые деструктивные операции с базой данных категорически не рекомендуется выполнять без предварительной проверки. Сначала сформируем и выполним проверочный SELECT, чтобы увидеть, какие именно строки попадают под условие.
Посмотрим на пример запроса через метапарсер 1С++, связывающий таблицу движений регистра «ОстаткиТМЦ» со справочником «Номенклатура»:
SELECT TOP 100
Номенклатура.descr [Наименование $Справочник.Номенклатура],
ОстаткиТМЦ.$ОстаткиТМЦ.Номенклатура [Номенклатура $Справочник.Номенклатура]
FROM $Регистр.ОстаткиТМЦ AS ОстаткиТМЦ
INNER JOIN $Справочник.Номенклатура AS Номенклатура
ON ОстаткиТМЦ.$ОстаткиТМЦ.Номенклатура = Номенклатура.ID
WHERE Номенклатура.ISMARK = 1
Если запрос тестируется напрямую в SQL Server Management Studio (или Query Analyzer) без метапарсера 1С++, используются физические имена таблиц и полей из словаря данных (файла 1cv7.dd):
SELECT TOP 100
Номенклатура.descr,
ОстаткиТМЦ.SP2343
FROM RG2351 AS ОстаткиТМЦ
INNER JOIN SC84 AS Номенклатура
ON ОстаткиТМЦ.SP2343 = Номенклатура.ID
WHERE Номенклатура.ISMARK = 1
Здесь RG2351 — таблица итогов регистра, SP2343 — колонка измерения «Номенклатура», а SC84 — физическая таблица справочника номенклатуры.
Когда мы убедились, что в выборку попадают только нужные строки, перейдем к конструкции удаления. В T-SQL существует два основных способа написания такой команды.
Вариант 1: Удаление с объединением таблиц (INNER JOIN)
В T-SQL при использовании JOIN в инструкции DELETE обязательно указывается алиас целевой таблицы сразу после ключевого слова DELETE. В противном случае сервер вернет синтаксическую ошибку:
DELETE ОстаткиТМЦ
FROM $Регистр.ОстаткиТМЦ AS ОстаткиТМЦ
INNER JOIN $Справочник.Номенклатура AS Номенклатура
ON ОстаткиТМЦ.$ОстаткиТМЦ.Номенклатура = Номенклатура.ID
WHERE Номенклатура.ISMARK = 1
Вариант 2: Удаление через подзапрос IN
Второй подход использует фильтрацию по списку идентификаторов. Алиас для целевой таблицы здесь не требуется:
DELETE FROM $Регистр.ОстаткиТМЦ
WHERE $ОстаткиТМЦ.Номенклатура IN (
SELECT Номенклатура.ID
FROM $Справочник.Номенклатура AS Номенклатура
WHERE Номенклатура.ISMARK = 1
)
Оптимизатор SQL Server в большинстве случаев построит для обоих вариантов идентичный план выполнения (Join). Однако при удалении огромных массивов данных (от десятков тысяч строк) выполнение единой транзакции может вызвать эскалацию блокировок всей таблицы и переполнение журнала транзакций (LDF). Для высоконагруженных систем безопаснее выполнять удаление порциями в цикле:
WHILE 1 = 1
BEGIN
DELETE TOP (5000) ОстаткиТМЦ
FROM $Регистр.ОстаткиТМЦ AS ОстаткиТМЦ
INNER JOIN $Справочник.Номенклатура AS Номенклатура
ON ОстаткиТМЦ.$ОстаткиТМЦ.Номенклатура = Номенклатура.ID
WHERE Номенклатура.ISMARK = 1;
IF @@ROWCOUNT = 0
BREAK;
END
Проанализируем последствия прямого удаления данных из таблиц СУБД в обход ядра 1С 7.7. Попытка просто выполнить DELETE из RA и RG скрывает в себе серьезные архитектурные риски:
RG) плюс движения текущего периода из таблицы движений (RA). Ручное удаление строк из обеих таблиц почти гарантированно приводит к расхождению математического баланса. Единственный надежный способ восстановить консистентность — пересчитать итоги (штатным «Тестированием и исправлением», пересчетом в режиме «Монопольно» или специализированными обработками полного пересчета регистров).ACTFLAGS в 1SJOURN): в общем журнале базы данных каждый проведенный документ содержит флаги активности по регистрам. Если движения из RA удалены напрямую, а маска активности в журнале осталась, система считает документ совершившим движения. При повторном проведении или отмене проведения такого документа возникнут разрывы итогов или непредсказуемые ошибки.DTxxxx). Штатная обработка «Удаление помеченных объектов» все равно не позволит физически стереть элементы номенклатуры, так как платформа найдет ссылки на них в проведенных и непроведенных документах.Таким образом, наиболее безопасная последовательность действий при регламентной зачистке регистров выглядит так: удалить записи только из таблицы движений (RAxxxx), после чего выполнить полный пересчет итогов регистра штатными средствами или административной обработкой, позволив платформе самостоятельно сформировать чистые и корректные срезы в таблице итогов.