Имя: Пароль:
1C
1С v8
Оптимизация программного кода по созданию номенклатуры, характеристик, свойств, документа
0 bebibo
 
18.11.22
11:46
Добрый день!
Народ, подскажите пожалуйста как можно усовершенствовать программный код, который читает файл эксель и:
1. Берет наименование Категории- проверяет есть такая в базе или нет (по наименованию). Если нет, то создает.
2. Берет наименование номенклатуры - проверяет есть такая в базе или нет (по наименованию). Если нет, то создает.
3. Далее создает элемент ПВХ.ДополнительныеРеквизитыИСведения или ищет в базе
4. Далее создает элемент справочника ЗначенияСвойствОбъектов
5. Далее создает другой элемент ПВХ.ДополнительныеРеквизитыИСведения или ищет в базе
6. Далее создает характеристику номенклатуры
7. Далее получает НаборСвойствХарактеристики и записывает туда эти 2 ПВХ
8. Последним пунктом создание документа "Установка цен", в который записываются цены на номенклатуру


Вот как тут можно оптимизировать? Если тут либо поиск, либо создание.
Вот у меня 500 элементов из экселя грузились 10 мин. Это очень долго
А всего 25 000 элементов - это целый день будет грузить..
1 Ryzeman
 
18.11.22
11:53
Делай замер производительности, смотри где у тебя узкие места и лишние циклы. Ту же эксельку как читаешь? Номенклатура сколько записывается?
А то мы тебе насоветуем, а окажется что у тебя по 20 секунд кривой запрос в подписке на запись номенклатуры выполняется... У меня по 5-10 минут выполнялись куда более трудоёмкие задачи с настоящим насилованием SQL.

Опять же что у тебя - SQL, файловая, Postgre?
2 bebibo
 
18.11.22
11:56
(1) Вот весь код
3 bebibo
 
18.11.22
11:56
(1)
&НаСервере
Процедура ЗагрузкаСПомощьюВнешнегоИсточникаДанныхНаСервере(ДвоичныеДанные, Имя)
    
    Путь = КаталогВременныхФайлов() + Имя  ;
    ДвоичныеДанные.Записать(Путь);
    ТабДокумент = Новый ТабличныйДокумент;
    Попытка
        ТабДокумент.Прочитать(Путь);
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат;
    КонецПопытки;
    
    
    ПоследняяСтрока = ТабДокумент.ВысотаТаблицы;
    
    ПоследняяКолонка = ТабДокумент.ШиринаТаблицы;
    
    ОбластьЯчеек = ТабДокумент.Область(1, 1, ПоследняяСтрока, ПоследняяКолонка);
    
    ИсточникДанных = Новый ОписаниеИсточникаДанных(ОбластьЯчеек);
    
    ПостроительОтчета = Новый ПостроительОтчета;
    
    ПостроительОтчета.ИсточникДанных = ИсточникДанных;
    
    Попытка
        ПостроительОтчета.Выполнить();    
        ТабЗначенийИзФайлаЭксель = ПостроительОтчета.Результат.Выгрузить();
    Исключение
        Сообщить(ОписаниеОшибки());
        Возврат;
    КонецПопытки;
    
    //Установить цену
    ДокУстановкиЦен = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
    ДокУстановкиЦен.Дата = ТекущаяДата();
    ДокУстановкиЦен.ПоказыватьХарактеристики = Истина;
    ДокУстановкиЦен.Автор = Пользователи.ТекущийПользователь();
    ДокУстановкиЦен.ЗаписыватьНовыеЦеныПоверхУстановленных = Истина;
    ДокУстановкиЦен.ПоказыватьДействующуюЦену = Истина;
    ДокУстановкиЦен.ПоказыватьОтклонениеЦен = Истина;
    ДокУстановкиЦен.ПоказыватьНовуюЦену = Истина;
    
    Для каждого стр из ТабЗначенийИзФайлаЭксель цикл
        НаименованиеНоменклатуры = СокрЛП(стр.Наименование);
        Если НаименованиеНоменклатуры = "" тогда
            Прервать;
        КонецЕсли;
        НаименованиеКатегории = СокрЛП(стр.Наименование);
        ПроизводительЗнач = СокрЛП(стр.Производитель);
        СуффиксЗнач = СокрЛП(стр.Суффикс);
        ЕдИзм =СокрЛП(стр.ЕдиницаИзмерения);
        ЦенаНом = СокрЛП(стр.Цена);
        ЕдИзм= Справочники.КлассификаторЕдиницИзмерения.НайтиПоНаименованию(ЕдИзм);
        //Создание категории        
        НайденаКатегория = Справочники.КатегорииНоменклатуры.НайтиПоНаименованию(НаименованиеКатегории,Истина);
        Если НЕ ЗначениеЗаполнено(НайденаКатегория)  тогда
            обКатегория = Справочники.КатегорииНоменклатуры.СоздатьЭлемент();        
            обКатегория.Наименование = НаименованиеКатегории;
            обКатегория.ТипНоменклатурыПоУмолчанию = Перечисления.ТипыНоменклатуры.Запас;        
            обКатегория.ИспользоватьХарактеристики = Истина;
            обКатегория.ЕдиницаИзмерения = ЕдИзм;    
            обКатегория.Записать();
            НайденаКатегория = обКатегория.Ссылка;
        КонецЕсли;    
        //Находим или создаем номенклатуру
        НайденаНоменклатура = Справочники.Номенклатура.НайтиПоНаименованию(НаименованиеНоменклатуры);
        Если НЕ ЗначениеЗаполнено(НайденаНоменклатура) тогда
            НовыйЭлементНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();
            НовыйЭлементНоменклатуры.Наименование =НаименованиеНоменклатуры;
            НовыйЭлементНоменклатуры.КатегорияНоменклатуры = НайденаКатегория;
            НовыйЭлементНоменклатуры.ЕдиницаИзмерения = ЕдИзм;
            НовыйЭлементНоменклатуры.НаименованиеПолное = НаименованиеНоменклатуры;
            НовыйЭлементНоменклатуры.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.Запас;
            НовыйЭлементНоменклатуры.ИспользоватьХарактеристики = Истина;
            НовыйЭлементНоменклатуры.Записать();
            НайденаНоменклатура = НовыйЭлементНоменклатуры.Ссылка;
        КонецЕсли;
        //    
        НоваяТЗ = новый ТаблицаЗначений;
        НоваяТЗ.Колонки.Добавить("Свойство");
        НоваяТЗ.Колонки.Добавить("Значение");
        
        СуффиксОбъект = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.СоздатьЭлемент();
        СуффиксОбъект.НаборСвойств = Справочники.НаборыДополнительныхРеквизитовИСведений.Справочник_Номенклатура;
        СуффиксОбъект.Наименование = "суффикс";
        СуффиксОбъект.Заголовок = "суффикс";
        СуффиксОбъект.ТипЗначения = Тип("СправочникСсылка.ЗначенияСвойствОбъектов");
        СуффиксОбъект.Виден = Истина;
        СуффиксОбъект.Доступен = Истина;
        СуффиксОбъект.Записать();
        Суффикс = СуффиксОбъект.Ссылка;
        
        ЗначениеРеквизита = Справочники.ЗначенияСвойствОбъектов.СоздатьЭлемент();
        ЗначениеРеквизита.Наименование = СуффиксЗнач;
        ЗначениеРеквизита.Владелец = Суффикс;
        ЗначениеРеквизита.Записать();
        
        Стр = НоваяТЗ.Добавить();
        Стр.Свойство = Суффикс;
        СсылкаНаЗначениеСуффикса = ЗначениеРеквизита.Ссылка;
        Стр.Значение = СсылкаНаЗначениеСуффикса;
        
        ///////////////////////
        Производитель = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Производитель", Истина);
        ЗначениеРеквизита = Справочники.ЗначенияСвойствОбъектов.СоздатьЭлемент();
        ЗначениеРеквизита.Наименование = ПроизводительЗнач;
        ЗначениеРеквизита.Владелец = Производитель;
        ЗначениеРеквизита.Записать();
        Стр = НоваяТЗ.Добавить();
        Стр.Свойство = Производитель;
        СсылкаНаЗначениеПроизводителя = ЗначениеРеквизита.Ссылка;
        Стр.Значение = СсылкаНаЗначениеПроизводителя;
        
        НоваяХарактеристика = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
        НоваяХарактеристика.Владелец = НайденаНоменклатура;
        НоваяХарактеристика.Наименование = СуффиксЗнач+", "+ПроизводительЗнач;
        НоваяХарактеристика.НаименованиеДляПечати = СуффиксЗнач+", "+ПроизводительЗнач;
        
        СтрДопРеквизит = НоваяХарактеристика.ДополнительныеРеквизиты.Добавить();
        СтрДопРеквизит.Свойство = Производитель;
        СтрДопРеквизит.Значение = СсылкаНаЗначениеПроизводителя;
        
        СтрДопРеквизит = НоваяХарактеристика.ДополнительныеРеквизиты.Добавить();
        СтрДопРеквизит.Свойство = Суффикс;
        СтрДопРеквизит.Значение = СсылкаНаЗначениеСуффикса;
        
        НоваяХарактеристика.Записать();
        
        //////////////////////
        ОбКат = НайденаКатегория.ПолучитьОбъект();
        обНаборСвойствХарактеристики = ОбКат.НаборСвойствХарактеристики.ПолучитьОбъект();
        СтрДопРеквизит = обНаборСвойствХарактеристики.ДополнительныеРеквизиты.Добавить();
        СтрДопРеквизит.Свойство = Производитель;
        СтрДопРеквизит = обНаборСвойствХарактеристики.ДополнительныеРеквизиты.Добавить();
        СтрДопРеквизит.Свойство = Суффикс;
        обНаборСвойствХарактеристики.Записать();
        ///////////////////////
        
        УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(НоваяХарактеристика.Ссылка,НоваяТЗ);    
        
        ТЧДок = ДокУстановкиЦен.Запасы.Добавить();
        ТЧДок.Номенклатура = НайденаНоменклатура;
        ТЧДок.Характеристика = НоваяХарактеристика.Ссылка;
        ТЧДок.ЕдиницаИзмерения =ЕдИзм ;
        ТЧДок.ВидЦены =Справочники.ВидыЦен.НайтиПоНаименованию("Основная цена покупки",Истина) ;
        ТЧДок.Цена = Число(ЦенаНом);
        //////////////////////////////////////////////            
    КонецЦикла;
    ДокУстановкиЦен.Записать(РежимЗаписиДокумента.Проведение);    
    УдалитьФайлы(Путь);
    Сообщить ("Загрузка завершена",);
КонецПроцедуры
4 Asmody
 
18.11.22
11:56
Пока нет кода, оптимизировать нечего.

Прочитать Excel в ТабДок, область ТабДока запихнуть в СКД, СКДой делать поиски-выборки. Чего СКДой не нашлось, создавать
5 bebibo
 
18.11.22
11:57
(3) База серверная
6 RomanYS
 
18.11.22
12:03
(3) Поиск должен быть запросом всего и сразу. Создаваемые элементы кидать в соответствие и получать оттуда по ключу поиска.
7 Ryzeman
 
18.11.22
12:09
(6) Соглашусь что запросом быстрее, но всё-же не понятно что у (3) именно тормозит так сильно. Для 500 строчек тут вообще ничего выдающегося. Вангую, что запись номенклатуры тормозит или ещё что
8 ass1c
 
18.11.22
12:12
Ты особо здесь ничего не оптимизируешь - основные потери идут при записи новых элементов.
При массовой загрузке используй при записи Объект.ОбменДанными.Загрузка = Истина; Но убедись что не потеряется нужный функционал. В твоем случае думаю так можно сделать.
9 Kassern
 
18.11.22
12:23
(0) Попробовать одним запросом получить нужные данные для записи.  Может РежимЗаписи.Загрузка как-то ускорит процесс. Установка цен можно одним документом сделать с перечнем видов цен и товаров.
10 Kassern
 
18.11.22
12:25
Можно также разить полученные данные для записи на пакеты. Далее уже создать Nое количество фоновых заданий и в них загружать в базу данные параллельно.
11 RomanYS
 
18.11.22
12:46
(7) НайтиПоНаименованию - это запрос к базе, у него их там 5 на один проход цикла, итого только на этом 2,5к запросов к базе
12 RomanYS
 
18.11.22
12:49
(8) совсем наоборот, основное время идёт на тупой поиск. Элементы могут вообще не создаваться (например при повторной загрузке), а тормоза всё равно будут
13 Ryzeman
 
18.11.22
12:51
(11) И?.. Если у него норм железо и норм настроено по феншую там в shared memory и всё такое, то хоть миллион циклов.
У меня есть обработка, которая строковую инфу через ПОДОБНО %Х% ищет в 20 таблицах в цикле, по 5к строк за раз. И ничего.
14 Ryzeman
 
18.11.22
12:51
В общем, даёшь скрины замера производительности в студию))
15 RomanYS
 
18.11.22
12:54
(13) Так не надо по 5к, сделай по одной)))
И откуда предположения, что у ТС нормальный сервер и настройки по феншую?
16 Ryzeman
 
18.11.22
12:56
(15) В смысле в 1 документе 5к строк и за 1 документ менеджеры пуляют по 5к циклов) Я это не писал, но переделывать было некогда, лень и страшно)
17 RomanYS
 
18.11.22
13:00
(16) 5к запросов по одной строке? Вам с ТС нужно клуб организовать, мучителей серверов)
18 Ryzeman
 
18.11.22
13:07
(17) нет, 5к строк, из каждая из которых пытается найти себя в 20 таблицах из прайсов поставщиков, справочника, артикула, наименования и т.п. "Я это не писал, но переделывать было некогда, лень и страшно)"
19 Dmitry1c
 
18.11.22
13:10
(0) распараллель на потоки, пусть в несколько потоков загружается ...
20 Kassern
 
18.11.22
13:12
(19) так вопрос, если один справочник разбить на потоки, то не будет блокировки таблицы при записи?
21 Сияющий Асинхраль
 
18.11.22
14:05
Код не сильно страшный. Даже большое количество поисков по строке может быть. Точно грузил таблицы по 20К штук номенклатуры, с созданием видов номенклатуры, доп. реквизитов и цен. Занимала загрузка минут 15. Единственное что, при большом количестве элементов НайтиПоНаименованию лучше не пользовать. Запрос для поиска даже одной позиции номенклатуры отрабатывает сильно быстрее (когда-то напарывался на это), ну и, если уж еще ускорить, то правильно написали - для начала поискать номенклатуру одним запросом по всему списку.
22 Dmitrii
 
гуру
18.11.22
14:30
(0)(3) Код конечно говно. Но чтобы радикально повысить его производительность, надо смотреть замеры.
Тупое переписывание кода "по правильному" может в итоге ничего не дать относительно общей скорости. А значит станет бессмысленным.

Иногда многое может зависеть от нюансов, и алгоритм можно оптимизировать, исходя из каких-то особенностей.
Например, одно дело, когда в 500-строчной таблице 500 разных номенклатур и каждая со своими индивидуальными категориями, сериями и характеристиками. То есть загрузка каждой строки сопряжена с поиском или созданием очередного элемента справочника. И совсем другое, когда в этой таблице всего 10 номенклатур с одной и той же категорией и серией, но каждая с 50-тью характеристиками. Третья история, когда нет никакой закономерности в соотношении номенклатур/категорий/серий/характеристик и каждая новая загружаемая обработкой таблица может быть совершенная разной по своему составу. А значит код придётся писать универсальный "на любые случаи".
23 VladZ
 
18.11.22
14:39
(0) Общий алгоритм должен быть такой:

1. Получить данные из файла в ТЗ
2. Идентифицировать нужные объекты.
3. Создать то, что не идентифицировано в п.2.
4. Загружаешь в документ.
24 Галахад
 
гуру
18.11.22
14:44
Да-а, код конечно тяжело читать. А правда категорий должно быть столько же сколько товаров? И множество суффиксов тоже необходимо?
25 Dmitrii
 
гуру
18.11.22
15:13
(24) Категория там может быть вообще одна. Так же как, например единица измерения - например везде "штуки".
Поэтому и предлагали выше сделать поиск одним запросом "всего и сразу", создание новых только для не найденных и созданные кидать в соответствие, где и искать потом по ключу.
Вопрос - даст ли это значительное ускорение? Если из 500-от строк загрузка 499-ти приводит к созданию и записи новых элементов, то подобная оптимизация особого эффекта не даст.
26 Сияющий Асинхраль
 
18.11.22
16:15
+(21) Ну и я бы отдельно за циклом сделал все Категории номенклатуры, свалил их в массив и оттуда проставлял в номенклатуру. Плюс то же самое сделал бы с производителями...
27 Галахад
 
гуру
18.11.22
16:25
(25) Вот и я про запись. Если я правильно понял, то у наименование товара и категории один и тот же источник. Что приводит к количеству категорий такому же как и у товаров. А это явно излишне. По суффиксам, тоже насколько понимаю излишние создание.
28 RomanYS
 
18.11.22
16:33
(26) только не в массив, а в соответствие...
29 mistеr
 
18.11.22
19:36
(0) 1. Читать не Эксель, а табдок.
2. Кэшировать найденное
3. Записывать по нескольку объектов в транзакции.
30 Lexandr
 
19.11.22
11:31
Убрать к черту "НайтиПоНаименованию". Была подобная задача. Так я весь справочник номенклатуры (поля ссылка и наименование) в запросе запихивал в временную таблицу, тут же другая временная таблица из табл.значений (полученная из табдок) и левым присоединением поиск по наименованию. Справочник номенклатуры был позиций тыщ сорок, а таблица с ключами поиска 200..800 строк. Работало достаточно шустро.
31 Garykom
 
гуру
19.11.22
11:37
(3) Скажи что будет если в базе есть элементы с одинаковым наименованием?
И да это довольно частая практическая ситуация.

Так что
РезультатЗапроса = Запрос.Выполнить.Выгрузить();
Если РезультатЗапроса.Количество=1 Тогда
// все ок один результат
ИначеЕсли РезультатЗапроса.Количество>1 Тогда
// упс несколько
Иначе
// не нашли, можно создать
КонецЕсли
32 Garykom
 
гуру
19.11.22
11:38
(30) Ага, особенно прикольно когда несколько одинаковых наименований и соединение выдает строк больше чем в файле ))
33 Garykom
 
гуру
19.11.22
11:39
(32)+ и хрен разберешь где и почему
а если делать группировку и постобработку что это тоже самое что запрос в цикле в итоге
34 RomanYS
 
19.11.22
11:43
(32) ну так никто тебе не мешает подготовить таблицу без дублей, а потом соединять.
35 Lexandr
 
19.11.22
12:09
(32) Ну как бы такое событие надо обязательно обмозговать на берегу. Варианты разные могут быть. Как ранее было сказано  сделать таблицу номенклатуры без повторяющихся или итоги по наименованию, а там разбираться. Но работа с результатом запроса  и "найтипонаименованию" - это две большие разницы.