При импорте данных из текстовых файлов (например, формата CSV с разделителем ";") часто возникает задача динамического создания колонок в таблице на форме с последующим заполнением их данными. Нередко разработчики сталкиваются с ситуацией, когда текстовые колонки заполняются корректно, а колонки ссылочного типа (такие как СправочникСсылка.Контрагенты или СправочникСсылка.Склады) после выполнения кода остаются пустыми, несмотря на то, что отладчик показывает успешное нахождение ссылок в базе данных. Рассмотрим, почему возникает эта проблема, как исправить логику заполнения и как кардинально ускорить работу алгоритма.
Выясним причину, по которой ссылочные данные пропадали из строк таблицы. Обратим внимание на фрагмент исходного цикла заполнения строки таблицы:
Для каждого ЭлементСтроки из КолонкиСтроки Цикл
Чч=Найти(В,";");
Если Чч=0 Тогда
ИмяКол=В;
Иначе
ИмяКол=Лев(В,Чч-1);
В=Сред(В,Чч+1);
КонецЕсли;
Если ЭлементСтроки.Ключ = "agentname" Тогда
НоваяСтрока.Контрагент = НайтиЭлем(ИмяКол,"К");
ИначеЕсли ЭлементСтроки.Ключ = "sklad" Тогда
НоваяСтрока.Склад = НайтиЭлем(ИмяКол,"С");
КонецЕсли;
НоваяСтрока[ЭлементСтроки.Ключ] = ИмяКол;
КонецЦикла;
В этом коде кроются сразу две логические ошибки, приводящие к затиранию данных:
"agentname" или "sklad", сначала срабатывает условие Если, и в реквизит Контрагент или Склад записывается ссылка на элемент справочника. Однако сразу после конструкции КонецЕсли выполняется безусловная строка НоваяСтрока[ЭлементСтроки.Ключ] = ИмяКол;. Она записывает исходное текстовое представление из файла (строку) в ту же самую колонку, перезаписывая только что найденную ссылку.КолонкиСтроки получается через реквизиты формы таблицы Накладные. В этот список входят и добавленные программно колонки "Контрагент" и "Склад". При обходе этих колонок в цикле также считывается очередная порция текста из файла (которой для этих колонок в самом файле физически нет), и в них записывается пустое или некорректное строковое значение.Для исправления ошибки нам необходимо разделить логику заполнения. Нам нужно заполнять текстовые реквизиты строк только тогда, когда они не соответствуют служебным колонкам, а обработку служебных колонок («Контрагент» и «Склад») изолировать. Также мы должны пропускать итерации цикла для тех колонок формы, которые заполняются расчетным путем, а не напрямую из файла.
Рассмотрим правильный вариант реализации цикла заполнения:
Для каждого ЭлементСтроки из КолонкиСтроки Цикл
// Если колонка является расчетной (Контрагент или Склад),
// пропускаем ее заполнение из файла, так как мы заполним ее при обработке ключевых полей
Если ЭлементСтроки.Ключ = "Контрагент" Или ЭлементСтроки.Ключ = "Склад" Тогда
Продолжить;
КонецЕсли;
// Выделяем очередное значение из строки файла
Чч = Найти(В, ";");
Если Чч = 0 Тогда
ИмяКол = В;
Иначе
ИмяКол = Лев(В, Чч - 1);
В = Сред(В, Чч + 1);
КонецЕсли;
// Распределяем данные по реквизитам
Если ЭлементСтроки.Ключ = "agentname" Тогда
// Заполняем строковое поле
НоваяСтрока.agentname = ИмяКол;
// Заполняем связанное ссылочное поле
НоваяСтрока.Контрагент = НайтиЭлем(ИмяКол, "К");
ИначеЕсли ЭлементСтроки.Ключ = "sklad" Тогда
// Заполняем строковое поле
НоваяСтрока.sklad = ИмяКол;
// Заполняем связанное ссылочное поле
НоваяСтрока.Склад = НайтиЭлем(ИмяКол, "С");
Иначе
// Заполняем все остальные динамические колонки
НоваяСтрока[ЭлементСтроки.Ключ] = ИмяКол;
КонецЕсли;
КонецЦикла;
В процессе обсуждения было отмечено, что подобная загрузка работает крайне медленно. Проанализируем причины низкой производительности исходного кода и разберем по шагам, как ускорить этот процесс в десятки и сотни раз.
В исходном коде чтение файла выполняется в процедуре &НаКлиенте, а функции поиска ссылок НайтиЭлем() и получения структуры колонок ПолучитьКолонки() вызываются с директивой &НаСервере. Вызов сервера на каждой итерации цикла по каждой строке файла порождает колоссальные сетевые задержки (пинг).
Решение: Перенесите всю логику чтения и обработки файла на сервер. На клиенте необходимо лишь выбрать файл и поместить его во временное хранилище, после чего передать адрес хранилища на сервер для дальнейшей обработки за один серверный вызов.
Ручной парсинг строк с помощью функций Найти, Лев, Сред работает медленно и неэффективно. В платформе 1С:Предприятие 8.3 для разделения строк по символу-разделителю предназначен быстрый встроенный метод СтрРазделить(), возвращающий готовый массив значений.
Поиск контрагентов методом НайтиПоНаименованию() и складов через запрос к базе данных на каждой строке файла создает огромную нагрузку на СУБД.
Решение: Примените кэширование в оперативной памяти:
В (&МассивУникальныхИмен)).Соответствие, где ключом будет наименование, а значением — ссылка.Заполнение строк непосредственно в реквизите формы Накладные вызывает постоянное обновление элементов интерфейса, что замедляет процесс. Перед заполнением переведите реквизит формы в прикладной объект в памяти сервера с помощью РеквизитФормыВЗначение("Накладные"), заполните полученную таблицу значений методами кэширования, а затем верните обратно на форму вызовом ЗначениеВРеквизитФормы().