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