Почему при загрузке номенклатуры из файла создаются дубли и как правильно организовать проверку?

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

При реализации механизмов загрузки данных из внешних источников (Excel, CSV, JSON) одной из самых приоритетных задач является контроль уникальности. Разработчики часто сталкиваются с ситуацией, когда, несмотря на наличие программных проверок, в справочнике Номенклатура все равно появляются дублирующиеся записи. Рассмотрим подробно, почему стандартные алгоритмы поиска могут давать сбой и как реализовать надежную проверку по нескольким полям, таким как Артикул и Наименование.

Разбор основной ошибки в логике запроса

Проанализируем типичный запрос, который часто используют для предварительной выборки существующих данных из базы. В исходной задаче автор использовал условие ГДЕ Артикул В(&МассивАртикулов) И Наименование В(&МассивНаименований). Выясним причину, по которой это условие работает некорректно для поиска дублей.

Использование оператора И (AND) заставляет систему искать только те элементы, у которых одновременно совпадает и артикул, и наименование из переданных списков. Однако в реальности дублем может считаться позиция, у которой совпадает только артикул (но отличается наименование) или только наименование (но отличается артикул). Если в базе уже есть товар с нужным артикулом, но другим названием, запрос с оператором И его просто «не увидит», и программа создаст новую запись.

Для корректной проверки заменим логику на оператор ИЛИ (OR):


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

Такой подход позволит выгрузить из базы все потенциально конфликтующие записи.

Тонкости поиска в массивах и работа с индексами

После получения данных из запроса мы обычно помещаем их в массивы для быстрого поиска внутри цикла обработки файла. Рассмотрим, на что важно обратить внимание при использовании метода Массив.Найти().

Метод Найти() возвращает индекс найденного элемента (число от 0 и выше) или значение Неопределено, если совпадений нет. Распространенная ошибка заключается в неправильной интерпретации результата 0. В языке 1С условие Если Индекс Тогда будет воспринято как Ложь, если индекс равен 0 (первый элемент массива). Поэтому проверку всегда нужно делать явной:


НайденныйАртикул = ИтоговыйМассивАртикулов.Найти(Строка.Артикул);
Если НайденныйАртикул <> Неопределено Тогда
    // Это дубль, нужно пропустить или обновить
    Продолжить;
КонецЕсли;

Также важно помнить, что поиск в массиве по умолчанию чувствителен к регистру. Если в справочнике артикул записан как "abc-123", а в файле — "ABC-123", метод Найти() вернет Неопределено. Чтобы избежать этой проблемы, рекомендуется приводить строки к одному регистру с помощью функции ВРег() или НРег().

Оптимизация производительности: использование объекта «Соответствие»

Линейный поиск в массиве (метод Найти()) при больших объемах данных работает медленно, так как системе приходится перебирать каждый элемент. Если в файле загрузки тысячи строк, время обработки будет расти в геометрической прогрессии. Посмотрим на более эффективный инструмент — объект Соответствие (Map).

Поиск в Соответствии осуществляется по хеш-таблице, что обеспечивает практически мгновенный результат вне зависимости от количества элементов. Перепишем наш алгоритм с использованием этой структуры:


// Создаем соответствия для быстрой проверки
СуществующиеАртикулы = Новый Соответствие;
СуществующиеНаименования = Новый Соответствие;

Для Каждого Стр Из ИтоговаяТаблицаЗапроса Цикл
    СуществующиеАртикулы.Вставить(СократЛП(Стр.Артикул), Истина);
    СуществующиеНаименования.Вставить(СократЛП(Стр.Наименование), Истина);
КонецЦикла;

// В цикле загрузки проверка будет выглядеть так:
Для Каждого СтрокаДанных Из Объект.ДанныеФайла Цикл
    ТекущийАртикул = СократЛП(СтрокаДанных.Артикул);
    ТекущееНаименование = СократЛП(СтрокаДанных.Наименование);
    
    Если СуществующиеАртикулы[ТекущийАртикул] <> Неопределено Тогда
        Сообщить("Пропуск: найден дубль по артикулу " + ТекущийАртикул);
        Продолжить;
    КонецЕсли;
    
    // Аналогично для наименования...
КонецЦикла;

Проблема лишних пробелов и нормализация данных

Выясним еще одну скрытую причину появления дублей — невидимые символы. При чтении данных из Excel в строках часто остаются лишние пробелы в начале или конце. Для 1С строки " 123" и "123 " — это разные значения.

Обязательно используйте функцию СократЛП() для всех строковых реквизитов, участвующих в проверке уникальности. Это простое правило предотвращает до 90% ошибок дублирования данных.

Алгоритм «умной» дозаписи в процессе загрузки

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

  1. Подготовка данных: Выгружаем все существующие артикулы и наименования в Соответствие.
  2. Цикл по строкам файла:
    • Очищаем данные от пробелов (СократЛП).
    • Проверяем наличие в Соответствии артикула.
    • Проверяем наличие в Соответствии наименования.
    • Если не найдено: создаем объект СправочникОбъект.Номенклатура.
    • Записываем объект в базу.
    • Важно: Сразу добавляем новый артикул и наименование в наши коллекции Соответствие, чтобы следующая строка файла с таким же товаром была распознана как дубль.

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

Для опытных разработчиков наиболее надежным методом является использование временных таблиц прямо в запросе. Вместо того чтобы передавать массивы в условие В (&Массив), мы можем загрузить всю таблицу из файла в запрос, поместить ее во временную таблицу и выполнить Левое Соединение со справочником Номенклатура по артикулу и наименованию. Это позволит одним запросом получить список всех строк, которые уже есть в базе, и тех, которые являются новыми.

Этот метод исключает ошибки, связанные с регистром (если SQL-база настроена как регистронезависимая) и значительно ускоряет обработку за счет оптимизации на уровне СУБД.

Подводя итог, отметим: корректная загрузка без дублей требует внимания к деталям — от использования ИЛИ в запросах до очистки строк от пробелов и выбора правильных объектов для поиска в памяти (Соответствие вместо Массив).

← На главную