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