Как найти и удалить лишние записи в независимом регистре сведений без перепроведения документов

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе эксплуатации информационных систем на платформе 1С:Предприятие нередко возникают ситуации, когда из-за ошибок в коде обработок проведения или внешних обработок в регистрах сведений накапливаются "мусорные" записи. Особенно остро эта проблема стоит в высоконагруженных системах, где документов тысячи, а активных пользователей — сотни. В таких условиях стандартное решение в виде перепроведения документов недопустимо из-за огромной нагрузки на сервер и блокировок таблиц. Разберем по шагам, как эффективно выявить лишние записи и безопасно удалить их программным способом.

Анализ проблемы и постановка задачи

Рассмотрим ситуацию: имеется независимый регистр сведений, данные в который записываются при проведении документа на основании его табличной части. Из-за логической ошибки в регистр попали строки, которых нет в документе (например, изменилось количество или номенклатура, а старые записи не затерлись). Наша задача — найти эти записи в РегистрСведений и удалить их, сохранив целостность данных и не мешая работе пользователей.

Шаг 1. Поиск лишних записей с помощью запроса

Для того чтобы определить, какие записи в регистре являются лишними, проанализируем механизм левого соединения. Мы будем использовать регистр сведений как основную таблицу, а табличную часть документа — как присоединяемую. Если для записи из регистра не найдется соответствующей строки в документе, поля документа в результате запроса примут значение NULL.

Разберем типичную ошибку: попытку наложить отбор по конкретному документу и проверку на NULL в одном запросе без использования временных таблиц. Выясним причину, почему это не работает. Прямое условие ГДЕ ТЧ.Ссылка = &Ссылка превращает левое соединение во внутреннее, и все строки с NULL просто отсекаются. Чтобы этого избежать, используем временные таблицы.

Рассмотрим правильную структуру запроса:


ВЫБРАТЬ
    ТЧ.Номенклатура КАК Номенклатура,
    ТЧ.Количество КАК Количество,
    ТЧ.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Документ
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
ГДЕ
    ТЧ.Ссылка В(&МассивДокументов);

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Регистр.Период КАК Период,
    Регистр.Номенклатура КАК Номенклатура,
    Регистр.ДокументОснование КАК ДокументОснование
ИЗ
    РегистрСведений.МойРегистр КАК Регистр
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Документ КАК ТЧ
        ПО Регистр.ДокументОснование = ТЧ.Ссылка
            И Регистр.Номенклатура = ТЧ.Номенклатура
            И Регистр.Количество = ТЧ.Количество
ГДЕ
    ТЧ.Ссылка ЕСТЬ NULL
    И Регистр.ДокументОснование В(&МассивДокументов)

В этом примере мы сначала помещаем данные "правильного" документа во временную таблицу ВТ_Документ, а затем соединяем её с данными регистра. Условие ГДЕ ТЧ.Ссылка ЕСТЬ NULL позволяет нам выбрать именно те записи регистра, для которых в документе нет аналогов по ключевым полям (Номенклатура, Количество, Ссылка).

Шаг 2. Особенности работы с полями типа Количество

Проанализируем ситуацию, когда в регистре ресурсов больше, чем в табличной части документа (например, КоличествоПлан и КоличествоФакт). В этом случае соединение нужно выполнять по тем полям, которые определяют уникальность записи с точки зрения бизнес-логики. Если лишняя запись отличается только числовым значением, обязательно включаем его в условие связи ПО. Если в регистре два поля количества, возможно, потребуется сделать два разных запроса или использовать сложные условия ИЛИ, чтобы точно идентифицировать "мусор".

Шаг 3. Программное удаление записей

После того как мы получили список лишних записей, переходим к их удалению. Для независимых регистров сведений основным инструментом является РегистрСведенийНаборЗаписей. Помните, что метод Записать() с предварительно установленным отбором заменяет все записи в БД, соответствующие этому отбору, на те, что содержатся в наборе в памяти. Если набор пустой — записи удаляются.

Посмотрим на пример кода для удаления:


Для Каждого СтрокаИзСписка Из РезультатЗапроса Цикл
    НаборЗаписей = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
    
    // Устанавливаем фильтры по всем измерениям регистра
    НаборЗаписей.Отбор.Период.Установить(СтрокаИзСписка.Период);
    НаборЗаписей.Отбор.Номенклатура.Установить(СтрокаИзСписка.Номенклатура);
    НаборЗаписей.Отбор.ДокументОснование.Установить(СтрокаИзСписка.ДокументОснование);
    
    // Записываем пустой набор, что приведет к удалению записи
    НаборЗаписей.Записать(Истина); 
КонецЦикла;

Важный нюанс: в высоконагруженной базе удаление каждой записи по отдельности в цикле может быть медленным. Если лишних записей очень много (десятки тысяч), эффективнее будет сгруппировать их по ДокументОснование и производить удаление наборами в разрезе документов, предварительно рассчитав правильные движения и перезаписав их целиком за один раз.

Шаг 4. Оптимизация для высоконагруженных систем

Поскольку наша база активно используется (до 10 тысяч пользователей), выполнение массовых операций требует осторожности. Разберем рекомендации по оптимизации:

  1. Порционность: Не пытайтесь обработать все 10 000 документов одним махом. Реализуйте обработку пакетами, например, по 50-100 документов за одну транзакцию.
  2. Транзакции: Используйте блок НачатьТранзакцию() ... ЗафиксироватьТранзакцию() для обеспечения целостности, но не делайте транзакции слишком длинными, чтобы не блокировать таблицы РегистрСведений надолго.
  3. Паузы: Между выполнением пакетов удаления можно вставить небольшие паузы (Приостановить), чтобы дать возможность системе обработать накопившиеся запросы от реальных пользователей.
  4. Режим записи: При использовании НаборЗаписей.Записать() всегда проверяйте необходимость вызова обработчиков событий (параметр Замещать). В данном случае нам нужно полное замещение текущих данных в БД данными набора.

Резюме

Для очистки независимого регистра от лишних записей мы использовали комбинацию мощного механизма запросов 1С и объектной модели работы с данными. Ключевым моментом является правильное построение ЛЕВОГО СОЕДИНЕНИЯ с проверкой на NULL, что позволяет математически точно вычислить расхождения между документом и регистром. Программное удаление через НаборЗаписей гарантирует корректность операции, а пакетная обработка позволяет выполнить задачу даже в условиях критических нагрузок на систему.

← На главную