В процессе эксплуатации информационных систем «1С:Предприятие» нередко возникают ситуации, когда в базу данных попадают дублирующиеся документы. Это может произойти из-за ошибок интеграции, некорректного импорта данных или человеческого фактора. Одной из самых частых задач является очистка системы от таких дублей: необходимо найти все повторяющиеся документы (например, по признаку Контрагент), но при этом сохранить в базе только один — самый актуальный, имеющий наиболее позднюю дату.
В этой статье мы подробно рассмотрим, как реализовать этот алгоритм, проанализируем структуру запросов и разберем программные методы обработки объектов, которые позволят безопасно и эффективно навести порядок в данных.
Перед тем как приступать к написанию кода, нам необходимо четко определить, что именно мы считаем дублем. В нашем примере основным критерием является реквизит Контрагент. Если в системе существует несколько документов одного типа с одинаковым контрагентом, мы считаем их дублями. Наша цель — оставить только тот документ, у которого значение реквизита Дата является максимальным среди всех документов данного контрагента.
Рассмотрим ситуацию на примере: у нас есть три документа по «Петрову» от 09.09, 15.09 и 20.09. Нам нужно пометить на удаление документы от 09.09 и 15.09, а документ от 20.09 оставить нетронутым.
Наиболее производительный способ решения этой задачи в 1С — использование языка запросов. Мы можем одним пакетом данных получить все ссылки на документы, которые подлежат удалению. Рассмотрим эффективную схему запроса, использующую временные таблицы и левое соединение.
Разберем логику построения запроса:
Посмотрим на пример кода запроса:
// 1. Выбираем последние даты для каждого контрагента
ВЫБРАТЬ
Док.Контрагент КАК Контрагент,
МАКСИМУМ(Док.Дата) КАК ПоследняяДата
ПОМЕСТИТЬ ВТ_ПоследниеДаты
ИЗ
Документ.МойДокумент КАК Док
СГРУППИРОВАТЬ ПО
Док.Контрагент;
// 2. Выбираем все документы, которые НЕ являются последними
ВЫБРАТЬ
Док.Ссылка КАК Ссылка
ИЗ
Документ.МойДокумент КАК Док
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПоследниеДаты КАК ВТ_Последние
ПО Док.Контрагент = ВТ_Последние.Контрагент
И Док.Дата = ВТ_Последние.ПоследняяДата
ГДЕ
ВТ_Последние.Контрагент ЕСТЬ NULL;
В результате выполнения этого запроса мы получим список ссылок на все документы, которые не являются «последними» для своих контрагентов. Использование ЛЕВОЕ СОЕДИНЕНИЕ с условием ГДЕ ... ЕСТЬ NULL позволяет отсечь «правильные» документы и оставить только те, которые подлежат удалению.
Проанализируем важный технический момент. В системе 1С Дата имеет точность до секунды. Однако в рамках одной секунды может быть создано несколько документов. Если мы будем опираться только на дату, запрос может вернуть несколько документов как «последние», если они записаны в одну и ту же секунду. В объектной модели 1С для решения этой проблемы существует понятие МоментВремени, который включает в себя дату и уникальный идентификатор ссылки, гарантируя строгую последовательность.
Для более точного определения последнего документа в запросе можно использовать дополнительные критерии, например, сравнивать ссылки или использовать автоинкрементные номера, если логика конфигурации это позволяет. Однако в большинстве практических случаев точности по секундам достаточно.
После того как мы получили список ссылок, нам необходимо программно установить пометку удаления. Важно помнить, что простое изменение реквизита ПометкаУдаления не всегда является корректным, если документ уже проведен. Правильнее использовать специализированные методы объекта.
Рассмотрим, как это реализовать в цикле обхода результата запроса:
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Получаем объект документа по ссылке
ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Используем метод УстановитьПометкуУдаления.
// Это автоматически снимет документ с проведения и очистит движения.
Попытка
ДокументОбъект.УстановитьПометкуУдаления(Истина);
// Если нужно просто пометить без проверок, можно использовать:
// ДокументОбъект.ПометкаУдаления = Истина;
// ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Исключение
Сообщить("Не удалось обработать документ: " + Выборка.Ссылка + " Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Обратите внимание на использование метода УстановитьПометкуУдаления(Истина). Это «золотой стандарт» разработки на 1С. Данный метод не просто ставит «галочку», он инициирует стандартные механизмы платформы: проверку возможности удаления, отмену проведения и очистку регистров накопления и бухгалтерии. Если документ был проведен, его движения исчезнут, и баланс в отчетах останется корректным.
Если в вашей базе данных сотни тысяч документов, выполнение левого соединения «в лоб» может занять длительное время и заблокировать таблицы СУБД. В таких случаях мы рекомендуем использовать индексацию временных таблиц. Проанализируем пример оптимизированного запроса:
ВЫБРАТЬ
Док.Контрагент КАК Контрагент,
МАКСИМУМ(Док.Дата) КАК ПоследняяДата
ПОМЕСТИТЬ ВТ_ПоследниеДаты
ИЗ
Документ.МойДокумент КАК Док
СГРУППИРОВАТЬ ПО
Док.Контрагент;
// Индексация значительно ускоряет последующее соединение
ИНДЕКСИРОВАТЬ ПО
Контрагент,
ПоследняяДата;
После индексации выборка дублей пройдет значительно быстрее, так как СУБД будет использовать подготовленные индексы вместо полного сканирования таблиц.
Перед массовым удалением документов крайне важно выяснить причину их появления и проверить, не используются ли эти документы как основания для других объектов. Выясним причину: если на дублирующий документ (который мы хотим удалить) ссылается счет-фактура или запись в регистре сведений, простая пометка на удаление может оставить «битые ссылки» в системе.
Профессиональный подход подразумевает использование метода НайтиСсылки(). В современных конфигурациях на базе БСП (Библиотека стандартных подсистем) для этого существует встроенная обработка «Поиск и удаление дублей». Она умеет делать «склейку»: не просто удалять дубль, а переносить все ссылки со «плохого» документа на «хороший». Если вы разрабатываете собственное решение, старайтесь придерживаться аналогичной логики: сначала перенос зависимостей, затем удаление.
Мы рассмотрели полный цикл решения задачи: от построения логики запроса до программной реализации удаления объектов. Подведем итоги:
МАКСИМУМ для поиска даты.Попытка ... Исключение при программной записи объектов, чтобы избежать прерывания алгоритма из-за блокировок или ошибок доступа.УстановитьПометкуУдаления(Истина).Следуя этим правилам, вы сможете эффективно очистить информационную базу от избыточных данных, сохранив при этом целостность учета и актуальность документов.