Как сравнить данные из таблицы значений с базой данных и избежать дублирования документов

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

В процессе разработки на платформе «1С:Предприятие 8» часто возникает задача: на основании данных из внешней таблицы (ТЗ) создать документы в базе данных. При этом критически важно не допустить создания дублей, если документ по конкретному объекту (например, по номеру пломбы, серийному номеру или артикулу) уже существует. В этой статье мы подробно разберем, как эффективно сравнить поле из ТЗ со значениями в базе данных, используя запросы и механизмы платформы.

Проблема «Запроса в цикле»

Рассмотрим типичную ситуацию: у нас есть таблица значений УчётПломб, и нам нужно проверить каждую строку. Начинающие разработчики часто пытаются выполнить запрос внутри цикла обхода ТЗ. Проанализируем, почему это плохо. Если в вашей таблице 1000 строк, программа сделает 1000 отдельных обращений к серверу базы данных. Это создаст колоссальную нагрузку и замедлит работу системы. Наша задача — минимизировать количество обращений к БД, выполнив проверку одним «пакетом».

Способ 1. Использование временных таблиц (Рекомендуемый)

Этот метод считается наиболее профессиональным и производительным. Мы передаем всю нашу таблицу значений в запрос, помещаем её во временную таблицу и соединяем с данными информационной базы. Рассмотрим этот процесс по шагам.

Во-первых, важно помнить, что для передачи ТЗ в запрос её колонки должны иметь строго определенные типы данных. Если типы не заданы, запрос не сможет обработать таблицу. Создадим структуру запроса:


// Подготовим ТЗ (колонки должны быть типизированы)
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст = 
"ВЫБРАТЬ
|	ТзПломб.Пломба КАК Пломба,
|	ТзПломб.Дата КАК Дата
|ПОМЕСТИТЬ ВТ_ДанныеТЗ
|ИЗ
|	&ТзПломб КАК ТзПломб
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ВТ_ДанныеТЗ.Пломба КАК Пломба,
|	ВТ_ДанныеТЗ.Дата КАК Дата
|ИЗ
|	ВТ_ДанныеТЗ КАК ВТ_ДанныеТЗ
|		ЛЕВОЕ СОЕДИНЕНИЕ Документ.ДвижениеПломб.Пломбы КАК СуществующиеПломбы
|		ПО ВТ_ДанныеТЗ.Пломба = СуществующиеПломбы.Пломба
|			И (НЕ СуществующиеПломбы.Ссылка.ПометкаУдаления)
|			И (СуществующиеПломбы.Ссылка.ВидОперации = &ВидОперации)
|ГДЕ
|	СуществующиеПломбы.Пломба ЕСТЬ NULL";

Запрос.УстановитьПараметр("ТзПломб", УчётПломб);
Запрос.УстановитьПараметр("ВидОперации", Перечисления.ВидыОперацийДвиженийПломб.Испорчена);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Здесь создаем только те документы, которых еще нет в базе
    НовыйДок = Документы.ДвижениеПломб.СоздатьДокумент();
    // Заполнение реквизитов...
    НовыйДок.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;

Почему это работает? Мы использовали ЛЕВОЕ СОЕДИНЕНИЕ. Система берет все строки из нашей ТЗ и ищет соответствия в базе данных. Если соответствие не найдено, в полях таблицы базы данных будет значение NULL. Условие ГДЕ СуществующиеПломбы.Пломба ЕСТЬ NULL отсекает те записи, которые уже есть в системе, оставляя только уникальные данные для обработки.

Способ 2. Использование массива в параметрах запроса

Если вам не нужно передавать много дополнительных данных из ТЗ, а требуется только проверить существование объектов по списку ключей (например, по списку номеров пломб), можно использовать массив.

  1. Выгрузим колонку с ключевым полем из ТЗ в массив: МассивПломб = УчётПломб.ВыгрузитьКолонку("Пломба");.
  2. Передадим этот массив в запрос и получим список уже существующих пломб.
  3. При обходе ТЗ проверим, входит ли пломба в «черный список» существующих.

Разберем пример реализации этого метода:


МассивПломб = УчётПломб.ВыгрузитьКолонку("Пломба");

Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ РАЗЛИЧНЫЕ
|	ДвижениеПломбПломбы.Пломба КАК Пломба
|ИЗ
|	Документ.ДвижениеПломб.Пломбы КАК ДвижениеПломбПломбы
|ГДЕ
|	ДвижениеПломбПломбы.Пломба В(&МассивПломб)
|	И НЕ ДвижениеПломбПломбы.Ссылка.ПометкаУдаления";

Запрос.УстановитьПараметр("МассивПломб", МассивПломб);
ТаблицаСуществующих = Запрос.Выполнить().Выгрузить();
// Для ускорения поиска проиндексируем таблицу
ТаблицаСуществующих.Индексы.Добавить("Пломба");

Для Каждого СтрТЗ Из УчётПломб Цикл
    ПараметрыПоиска = Новый Структура("Пломба", СтрТЗ.Пломба);
    Если ТаблицаСуществующих.НайтиСтроки(ПараметрыПоиска).Количество() > 0 Тогда
        Сообщить("Пломба " + СтрТЗ.Пломба + " уже обработана, пропускаем.");
        Продолжить;
    КонецЕсли;
    
    // Логика создания документа
КонецЦикла;

Оптимизация поиска: Индексация

Когда мы работаем с большими объемами данных в памяти (объекты ТаблицаЗначений), стандартный поиск методом Найти() или НайтиСтроки() может работать медленно, так как он выполняет линейный перебор строк. Чтобы ускорить процесс, обязательно используйте метод Индексы.Добавить("ИмяКолонки"). Это создает хэш-таблицу, поиск по которой происходит практически мгновенно.

Архитектурные советы по контролю уникальности

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

Заключение

Мы выяснили, что наиболее эффективным способом сравнения ТЗ с данными базы является использование временных таблиц и механизма соединений в одном запросе. Это избавляет систему от избыточных вызовов и гарантирует высокую скорость работы. Если задача разовая и данных немного, допустимо использование массива в параметре В(&Массив), но всегда помните об индексации результатов для быстрого поиска.

← На главную