При разработке механизмов импорта данных из внешних файлов, таких как таблицы Excel, начинающие программисты часто сталкиваются с ситуацией, когда запрос "не видит" переданный параметр или возвращает пустой результат, даже если данные в файле присутствуют. В этой статье мы подробно разберем, почему возникает такая проблема, как правильно сопоставлять типы данных и как оптимизировать процесс загрузки в регистры сведений.
Разберем ситуацию, представленную в вопросе. Программист считывает значение ячейки из Excel-файла с помощью COM-объекта и пытается передать это значение в параметр запроса &Ссылка. Основная ошибка здесь кроется в фундаментальном различии типов данных. Когда мы получаем значение из ячейки Excel (например, название товара "Молоко"), 1С интерпретирует его как строку. Однако в запросе к базе данных при сравнении с полем Номенклатура.Ссылка система ожидает объект типа СправочникСсылка.
Даже если строка в Excel полностью совпадает с наименованием в 1С, для системы это разные сущности. Ссылка — это уникальный идентификатор (GUID), а наименование — это просто текстовое описание. Поэтому попытка выполнить условие ГДЕ Ссылка = &СтрокаИзExcel всегда будет давать ложный результат.
Самый простой способ исправить ситуацию — это найти ссылку на объект в справочнике 1С, используя полученное из Excel текстовое значение. Рассмотрим, как изменить код, чтобы параметр установился корректно. Для этого воспользуемся методом НайтиПоНаименованию().
Проанализируем исправленный фрагмент кода:
// Считываем наименование из Excel
НаименованиеИзФайла = Док.Sheets(1).Cells(СчСтроки, 8).Value;
// Ищем соответствующую ссылку в справочнике Номенклатура
СсылкаНаТовар = Справочники.Номенклатура.НайтиПоНаименованию(НаименованиеИзФайла, Истина);
// Проверяем, найден ли товар в базе
Если СсылкаНаТовар.Пустая() Тогда
Сообщить("Товар не найден: " + НаименованиеИзФайла);
Продолжить; // Пропускаем итерацию, если товар не найден
КонецЕсли;
// Теперь передаем в запрос именно ССЫЛКУ, а не строку
Запрос.УстановитьПараметр("Ссылка2", СсылкаНаТовар);
Обратите внимание, что во втором параметре метода НайтиПоНаименованию(Наименование, ТочноеСоответствие) мы указали Истина. Это гарантирует, что система найдет только полное совпадение, исключая ошибки при частичном вхождении слов.
В исходном примере также была допущена ошибка в структуре самого запроса. Отсутствовала секция ГДЕ, что делало условие нерабочим. Рассмотрим правильную структуру запроса для проверки наличия записи в регистре сведений:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Прайс.Номенклатура КАК Номенклатура
|ИЗ
| РегистрСведений.Прайс КАК Прайс
|ГДЕ
| Прайс.Номенклатура = &НоменклатураСсылка";
Запрос.УстановитьПараметр("НоменклатураСсылка", СсылкаНаТовар);
РезультатЗапроса = Запрос.Выполнить();
Если нам нужно проверить иерархию (например, входит ли товар в определенную группу), мы используем оператор В ИЕРАРХИИ, но в большинстве случаев для точечной проверки записи в регистре достаточно обычного равенства =.
Важно понимать, что выполнение запроса внутри цикла Пока ... Цикл — это серьезная ошибка производительности. Если в вашем файле Excel 1000 строк, программа выполнит 1000 обращений к базе данных, что значительно замедлит работу. Рассмотрим, как сделать процесс более эффективным по шагам:
Пример подготовки данных для пакетной обработки:
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
ТаблицаДанных.Колонки.Добавить("Штрихкод", Новый ОписаниеТипов("Строка"));
// Заполняем таблицу из Excel
Пока СокрЛП(НоменклатураТекст) <> "" Цикл
НоваяСтрока = ТаблицаДанных.Добавить();
НоваяСтрока.Наименование = СокрЛП(Док.Sheets(1).Cells(СчСтроки, 8).Value);
НоваяСтрока.Штрихкод = СокрЛП(Док.Sheets(1).Cells(СчСтроки, 2).Value);
СчСтроки = СчСтроки + 1;
// ... логика получения следующей строки
КонецЦикла;
// Передаем таблицу в запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ;
|ВЫБРАТЬ Спр.Ссылка ИЗ ВТ_Данные КАК ВТ
|ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Спр ПО ВТ.Наименование = Спр.Наименование";
Запрос.УстановитьПараметр("ТЗ", ТаблицаДанных);
В исходном коде автор использует РегистрыСведений.Прайс.СоздатьНаборЗаписей(). Это оправдано, если нужно перезаписать сразу много строк по определенному отбору. Однако для добавления одной новой записи или обновления существующей (когда мы обрабатываем товары по одному) гораздо удобнее и безопаснее использовать МенеджерЗаписи.
Выясним причину: при использовании набора записей без установленного отбора вы рискуете очистить весь регистр при вызове метода Записать(). Менеджер записи работает более точечно.
МенеджерЗаписи = РегистрыСведений.Прайс.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Номенклатура = СсылкаНаТовар; // Измерение
// Заполняем остальные поля (ресурсы)
МенеджерЗаписи.Цена = ЦенаИзExcel;
// Записываем с параметром Истина для замещения существующей записи
МенеджерЗаписи.Записать(Истина);
При получении данных из Excel через Value следует помнить о нескольких нюансах:
СокрЛП(Строка(Ячейка.Value)), чтобы избежать ошибок при поиске.СокрЛП().Quit(), нужно также обнулить переменную 1С, чтобы процесс удалился из оперативной памяти сервера или компьютера.Проанализируем правильное завершение работы:
Док.Application.Quit();
Док = Неопределено; // Освобождаем COM-объект
Подводя итог, можно сказать, что решение проблемы "невидимого параметра" заключается в строгом контроле типов данных. 1С — система со строгой типизацией в запросах, и автоматического преобразования строки "Товар №1" в ссылку на элемент справочника не произойдет. Всегда выполняйте предварительный поиск или используйте временные таблицы для массового сопоставления данных.