|   |   | 
| 
 | Оптимизация программного кода по созданию номенклатуры, характеристик, свойств, документа | ☑ | ||
|---|---|---|---|---|
| 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) Ну как бы такое событие надо обязательно обмозговать на берегу. Варианты разные могут быть. Как ранее было сказано  сделать таблицу номенклатуры без повторяющихся или итоги по наименованию, а там разбираться. Но работа с результатом запроса  и "найтипонаименованию" - это две большие разницы.     | 
| Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |