Конвертация данных хороший инструмент, которая позволяет быстро реализовать несложную перегрузку данных между различными базами 1С. С помощью конвертации данных реализованы перенос остатков из типовых конфигураций 1С 7.7 в типовые на базе 1С8.
Когда в очередной раз столкнулся с необходимостью переноса данных из 1С7.7 в 1С8, то столкнулся с проблемой. При выгрузке большого справочника (около 400 тыс. элементов. Все относительно конечно) семерка вылетает с ошибкой «недостаточно памяти…» Проблема в том, что обработка выгрузки использует объектную модель DOM для работы с XML. В результате все дерево XML до записи в файл хранится в памяти. Также столкнулся с тем, что при выгрузке большого числа элементов, чем дольше работает выгрузка тем медленнее она работает.
Решил немного переделать обработку выгрузки, чтобы исключить эти узкие места.
Первая доработка. Возможность формирования файла xml большого размера.
Доработка заключается в потоковой записи данных XML сразу в файл. На IS видел публикацию в которой автор писал, что использовал другой объект для работы с XML, но подход вроде такой же – запись сразу в файл.
Для потоковой записи в файл использую объект FileSystemObject Windows Script Host. Доработки следующие.
1.Добавить описание переменных
Перем ФСО;
Перем ХМЛФайл;
2.Изменить процедуру Выгрузить()
Процедура Выгрузить()
// Добавлено. Начало
ФСО = СоздатьОбъект("Scripting.FileSystemObject");
ХМЛФайл = ФСО.CreateTextFile(ИмяФайлаДанных, 1, 1);
// Добавлено. Конец
ВыполнитьВыгрузку();
// Добавлено. Начало
ХМЛФайл.Close();
// Добавлено. Конец
Если Форма.МодальныйРежим() = 0 Тогда
//Предупреждение("Выгрузка данных завершена.");
Сообщить("Выгрузка данных завершена.");
Иначе
Сообщить("Выгрузка данных завершена.");
КонецЕсли;
Форма.Параметр = СписокОшибок;
КонецПроцедуры // Выгрузить()
3.Изменить процедуру ВыгрузитьПоПравилу()
Весь текст процедуры не пишу. Изменения ближе к концу процедуры
Функция ВыгрузитьПоПравилу(Источник, Приемник, ВходящиеДанные, ИсходящиеДанные, ИмяПКО = "", УзелСсылки = "", ТолькоПолучитьУзелСсылки = 0,
НомерПКО = 0)
………………………………………………………………
// Изменено. Начало
// Запись объекта
//ДобавитьПодчиненный(rootNode, Приемник);
ХМЛФайл.WriteLine(Приемник.xml);
// Изменено. Конец
// Обработчик ПослеВыгрузкиВФайлОбмена
Если ПолучитьРеквизитПКО(НомерПКО, "ПослеВыгрузкиВФайл") = 1 Тогда
КодПравила = СокрЛП(ПолучитьРеквизитПКО(НомерПКО, "Код"));
Отказ = Шаблон("[ПКО_ПослеВыгрузкиВФайлОбмена_" + КодПравила + "(Источник, ВходящиеДанные, ИсходящиеДанные, ИмяПКО, Приемник, УзелСсылки)]");
Если Число(Отказ) = 1 Тогда
Возврат УзелСсылки;
КонецЕсли;
КонецЕсли;
Возврат УзелСсылки;
КонецФункции // ВыгрузитьПоПравилу()
4.Изменить процедуру ИнициализацияФайлаОбмена()
Процедура ИнициализацияФайлаОбмена()
УстановитьАтрибут(rootNode, "ВерсияФормата", "2.0");
УстановитьАтрибут(rootNode, "ДатаВыгрузки", ПолучитьДатуV8(ТекущаяДата(), ТекущееВремя()));
УстановитьАтрибут(rootNode, "НачалоПериодаВыгрузки", ПолучитьДатуV8(ДатаНачала));
УстановитьАтрибут(rootNode, "ОкончаниеПериодаВыгрузки", ПолучитьДатуV8(ДатаОкончания));
УстановитьАтрибут(rootNode, "ИмяКонфигурацииИсточника", мКонфигурацияИсточник);
УстановитьАтрибут(rootNode, "ИмяКонфигурацииПриемника", мКонфигурацияПриемник);
УстановитьАтрибут(rootNode, "ИдПравилКонвертации", мИд);
УстановитьАтрибут(rootNode, "Комментарий", "");
// Добавлено. Начало
ХМЛФайл.WriteLine("ВерсияФормата=" + """2.0"""); //NewNode.xm
ХМЛФайл.WriteLine("ДатаВыгрузки" + "=""" + Строка(ПолучитьДатуV8(ТекущаяДата(), ТекущееВремя())) + """");
ХМЛФайл.WriteLine("НачалоПериодаВыгрузки" + "=""" + Строка(ПолучитьДатуV8(ДатаНачала)) + """");
ХМЛФайл.WriteLine("ОкончаниеПериодаВыгрузки" + "=""" + Строка(ПолучитьДатуV8(ДатаОкончания)) + """");
ХМЛФайл.WriteLine("ИмяКонфигурацииИсточника" + "=""" + Строка(мКонфигурацияИсточник) + """");
ХМЛФайл.WriteLine("ИмяКонфигурацииПриемника" + "=""" + Строка(мКонфигурацияПриемник) + """");
ХМЛФайл.WriteLine("ИдПравилКонвертации" + "=""" + Строка(мИд) + """");
ХМЛФайл.WriteLine("Комментарий" + "=""""");
ХМЛФайл.WriteLine(">");
// Добавлено. Конец
………………………………………………………………
………………………………………………………………
// Добавлено. Начало
ХМЛФайл.WriteLine(УзелПравилаОбмена.xml);
// Добавлено. Конец
КонецПроцедуры // ИнициализацияФайлаОбмена()
5.Изменить процедуру ВыполнитьВыгрузку()
Процедура ВыполнитьВыгрузку()
…………………………………………………
rootNode = DOMDocument.createNode(1, "ФайлОбмена", "");
// Добавлено. Начало.
ХМЛФайл.WriteLine("<" + "ФайлОбмена");
// Добавлено. Конец
…………………………………………………..
// Изменено. Начало
//DOMDocument.appendChild(rootNode);
//DOMDocument.save(ИмяФайлаДанных);
ХМЛФайл.WriteLine("</" + "ФайлОбмена>");
// Изменено. Конец
ВывестиСообщение("Выгружено объектов: " + мСчетчикВыгруженныхОбъектов);
ВывестиСообщение("Окончание выгрузки: " + ТекущаяДата() + " " + ТекущееВремя());
КонецПроцедуры // ВыполнитьВыгрузку()
Вторая доработка. Оптимизация скорости выгрузки.
По мере выгрузки справочника делал простой замер скорости. Считал за сколько минут выгружается допустим 1000 объектов. И по этим данным делал примерный расчет общего времени выгрузки. И чем больше объектов выгружалось, тем больше становилось расчетное время выгрузки. 25 тыс – 4,5 часа. 50тыс. – 6 часов. На 100 тыс – уже 10 часов.
Сделал замер. На три однотипные строчки кода тратилось 78% времени.
УзелСсылки = ВыгруженныеОбъекты.Получить(КлючВыгружаемыхДанных);
ВыгруженныеОбъекты.Установить(КлючВыгружаемыхДанных, Нпп);
ВыгруженныеОбъекты.Установить(КлючВыгружаемыхДанных, УзелСсылки);
Изначально ВыгруженныеОбъекты – это список значений.
Заменил Список значений на индексированную таблицу из компоненты 1С++
Что сделать.
1. Изменил процедуру ПриОткрытии ()
Процедура ПриОткрытии()
// Добавлено. Начало
ЗагрузитьВнешнююКомпоненту("1cpp.dll");
// Добавлено. Конец
…………………………………………………………
КонецПроцедуры // ПриОткрытии()
2. Изменить процедуру ЗагрузитьПКО()
Процедура ЗагрузитьПКО(Знач Порядок = "")
мТаблицаПравилКонвертацииОбъектов.НоваяСтрока();
мТаблицаПравилКонвертацииОбъектов.ТекущаяСтрока(мТаблицаПравилКонвертацииОбъектов.КоличествоСтрок());
//мТаблицаПравилКонвертацииОбъектов.Выгруженные = СоздатьОбъект("СписокЗначений");
ИндТаблица = СоздатьОбъект("ИндексированнаяТаблица");
ИндТаблица.НоваяКолонка("КлючВыгружаемыхДанных");
ИндТаблица.НоваяКолонка("Ссылка");
ИндТаблица.ДобавитьИндекс("Ключ","КлючВыгружаемыхДанных");
мТаблицаПравилКонвертацииОбъектов.Выгруженные = ИндТаблица;
…………………………………………………………………………………………………
КонецПроцедуры // ЗагрузитьПКО()
3. Изменить процедуру ВыгрузитьПоПравилу()
Места изменния найти поиском по слову ВыгруженныеОбъекты
Функция ВыгрузитьПоПравилу(Источник, Приемник, ВходящиеДанные, ИсходящиеДанные, ИмяПКО = "", УзелСсылки = "", ТолькоПолучитьУзелСсылки = 0,
НомерПКО = 0)
……………………………………………………………
Если НеЗапоминатьВыгруженные = 0 Тогда
// Изменено. Начало
//УзелСсылки = ВыгруженныеОбъекты.Получить(КлючВыгружаемыхДанных);
НомерСтроки = ВыгруженныеОбъекты.НайтиСтроку("Ключ", 1);
Если НомерСтроки = 0 Тогда
УзелСсылки = "";
Иначе
УзелСсылки = ВыгруженныеОбъекты.ПолучитьЗначение(НомерСтроки, "Ссылка");
КонецЕсли;
// Изменено. Конец
Если ПустоеЗначение(УзелСсылки) = 0 Тогда
Возврат УзелСсылки;
КонецЕсли;
КонецЕсли;
…………………………………………………………………….
// Это позволит избежать циклических ссылок
Если НеЗапоминатьВыгруженные = 0 Тогда
// Изменено. Начало
//ВыгруженныеОбъекты.Установить(КлючВыгружаемыхДанных, Нпп);
ВыгруженныеОбъекты.НоваяСтрока();
ВыгруженныеОбъекты.КлючВыгружаемыхДанных = КлючВыгружаемыхДанных;
ВыгруженныеОбъекты.Ссылка = Нпп;
// Изменено. Конец
КонецЕсли;
……………………………………………………………
ВыгрузитьСвойства(Источник, Приемник, ВходящиеДанные, ИсходящиеДанные, НомерПКО, ПолучитьРеквизитПКО(НомерПКО, "СвойстваПоиска"),
УзелСсылки, , , ИмяПредопределенногоЭлемента, ВыгрузитьТолькоСсылку);
// Изменено. Начало
//ВыгруженныеОбъекты.Установить(КлючВыгружаемыхДанных, УзелСсылки);
ВыгруженныеОбъекты.НоваяСтрока();
ВыгруженныеОбъекты.КлючВыгружаемыхДанных = КлючВыгружаемыхДанных;
ВыгруженныеОбъекты.Ссылка = УзелСсылки;
// Изменено. Конец
……………………………………………………………
КонецФункции // ВыгрузитьПоПравилу()
После этих изменений выгрузка моего справочника выполнилась за 2 с половиной часа.
С чем так и не смог справиться, так это с ошибкой о нехватке памяти, когда для ПКО НЕ стоит свойство «не запоминать выгруженные. Растет таблица закэшированных xml-фрагментов ссылок на выгруженные объекты. Пришлось для ПКО моего большого справочника поставить «не запоминать выгруженные объекты».
Сейчас попробуем) для начала неплохо было бы упомянуть, что сперва нужно зарегистрировать библиотеку scrrun.dll.
В командной строке под админом:
cd c:windwossystem32
regsvr32 scrrun.dll
Здорово! А никто не подскажет как бы подобным волшебным образом ускорить типовой обмен в 8-ке?
(2) ander_, на практике с медленной работой тпового обмена не сталкивался. По крайней мере обмен не выполнялся настолько долго, что бы стать проблемой. Нужно смотреть конкретно на вашем случае, где узкое место при обмене. Точнее не отвечу.
Не понял где поставить в ПКО не запоминать выгруженные объекты
(4) skt-roman, это галочка в свойствах ПКО. При наличии этой галочки, для объекта в файл XML будет формироваться полная выгрузка по правилам, каждый раз когда объект встречается при выгрузке. А если галочка НЕ стоит — то в файл объект записывается полностью только один раз, а дальше, если встречается повторно — записывается только описание «ссылки» с полями поиска.
Сделал первую доработку. Виснет на выгрузке счета 41.1, оставил выгрузку по правилу только этот счет — виснет выгрузив 2284 объекта
(6) skt-roman, общий смысл первой доработки — исключить хранение в памяти объекта большого размера. это реализовано потоковой записью в файл. Общий алгоритм выгрузки не затронут.
Если я правильно понял, выгружаете только этот один счет. А если выгружается 2284 объекта для одного счета — то это очень похоже на зацикливание.
Предположу, что для ПКО какого-то объекта, который попал в выгрузку, снята галочка «не запоминать выгруженные» (о чем говорили в (4) и (5)), а у выгружаемого объекта есть реквизит, который ссылается на этот же объект. По крайней мере сталкивался с таким. Точнее скажет только отладчик. Если не ошибаюсь, при записи сразу в файл, этот файл можно сразу открыть. Посмотрите по содержимому, что именно выгружается для вашего счета.
По поводу галочки «не запоминать выгруженные». в коде обработки выгрузки есть такой комментарий:
По этому ставить этот флаг у ПКО объекта нужно, только в крайнем случае, если это действительно необходимо.
(7) Just4Fun, выгрузка без доработки это отрабатывает нормально, странно.
Когда смотрел стандартную выгрузку из 7.7 бухгалтерию в Бух 3.0
Особенно выгрузку остатков по счету. Кто придумывал такой алгоритм работы…
Создается таблица потом в первом прогоне уменьшается в два раза и потом еще раза три — четыре в цикле крутиться.
Пока это не исправить все остальное: мелочи.
Примерно до 20000 выгрузка шла быстро, потом стала выгружать примерно по 10 объектов в секунду. Никто не сталкивался ?
(10) buy_sale, Сталкивался. Мой случай описан во «второй доработке» из этой статьи. Уже не помню свои исходные данные, но прогнозная оценка времени выгрузки составляла больше 10 часов. После доработки выгрузка выполнилась за 2,5 часа
Если применили эту доработку, то понять причину поможет только отладчик.
Уважаемый а вы уверены, что это тождественнно
мне кажется надо
иначе зацикливается на одном элементе
исправил так как указано — 480 000 за 90 мин !) До этого было 2 суток и падало с ошибкой. База 1,5 гиг свернутая
Аплодирую стоя !
В ВыгрузитьПоПравилу добавил для спр Номенклатура ( У меня 160000 элементов)
….
Показать
(12) buy_sale,
«Уважаемый а вы уверены, что это тождественнно » —
За давностью лет, подтвердить или опровергнуть не смогу. Поскольку вместо типового списка значений задействовал индексированную таблицу из 1СPP, то возможно сделал так по возможному синтаксису метода этого объекта. Уже не помню, скорее всего в методе допускалось указание имени или номера колонки, потому так и написал.
«Аплодирую стоя ! »
Пожалуйста.
Есть проблема : выгрузка зависает на 497144 объектах. После загрузки БП3 пишет Extra Content at the end of document. Соответственно документы остатков по самому большому справочнику номенклатуры (150 000 элементов) не появляются.
даже когда маленький файл гружу все равно конец обрезается (((
https://yadi.sk/i/8b7xdzFRsm4Hm
(14) buy_sale,
«даже когда маленький файл гружу все равно конец обрезается»
Нужно проверить, корректно ли «закрывается» файл, т.е. все ли закрывающие теги на месте. На маленьком файле должно быть видно. Если файл действительно небольшой, то можно открыть в IE — должен корректно отобразиться в виде дерева.
В своей практике сталкивался с проблемой — «некорректный» символ в строке (в наименовании элемента справочника) — долго не мог понять, в чем причина.
«Соответственно документы остатков по самому большому справочнику номенклатуры (150 000 элементов) не появляются.»
Может конечно у вас и не одним документом все грузится, но на всякий случай посоветовал бы ограничить количество строк на документ, например по 1-2 тыс. строк.. С документами с небольшим количеством строк потом и работать легче (отрывать, проверять, проводить и т.п.).
Еще как вариант оптимизации, выгрузить отдельно ваш большой справочник, только то что возвращает запрос для выгрузки остатков. А потом отдельно выгружать документы остатков с включенным флагом (в ПКО) «не выгружать объекты по ссылкам…»
(14) buy_sale, посмотрел внимательно на скрин с ошибкой. Нет ли в обработчике «после загрузки данных» чтения каких-либо дополнительных данных из файла?
Добрый день! А 1CPP.dll в Windows 10 работает? Что то не могу зарегистрировать dll-ку.
Господа, подскажите что делаю не так. Не могу зарегистрировать 1СРР через regsvr32. Скачал с 1cpp.ru. Пробовал на Виндовс 10, 7 и ХР. Пушу в командной строке от Админа regsvr32 «c:1CPP.dll» , ошибка Не найден путь.
Вопрос снят! Просто закинул ее в BIN и в папку с базой!
Теперь другая ошибка при выгрузке:
мТаблицаПравилКонвертацииОбъектов.Выгруженные.УдалитьВсе();
{C:CONVERT_25V77EXP+.ERT(59116)}: Поле агрегатного объекта не обнаружено (УдалитьВсе)
(12)(12)(19) its_valera, добрый день.
Коллеги, всех приветствую. Использовали рекомендации из этой публикации на сложной задаче переноса данных из ТиС в УТ 11. Большое спасибо автору за идею и направление движения по оптимизации выгрузки! Пока у нас решение не заработало полноценно — зависает даже при отметке единственного правила «Номенклатура» на 27168м элементе. Будем пробовать рекомендации из комментариев.
А насчет ошибки, с которой Вы столкнулись — у нас такая тоже была. Вот что надо сделать. Вместо строки:
Нужен такой код:
Так как тип теперь стал ТаблицаЗначений.
Интересно, сделал доработку, перестали загружаться ссылочные реквизиты: например выгружаем справочник номенклатура, реквизит ед. измерения, в файле выгрузки есть единица измерения, выгружена один раз, далее ссылками заполняется, а в итоговой базе пусто. Если без доработки тогда — все нормально, и в файле выгрузки ед измерения выгружается каждый раз, без ссылок, хотя флаг » не запоминать объекты» не взведен.
УзелСсылки = ВыгруженныеОбъекты.ПолучитьЗначение(НомерСтроки, «Ссылка»); здесь получается не xml узел(типа dom объект), а просто Нпп…
Вроде разобрался:
вместо НомерСтроки = ВыгруженныеОбъекты.НайтиСтроку(«Ключ», 1); надо НайтиСтроку(«Ключ», КлючВыгружаемыхДанных, 1);
Так как в таблицу добавляется 2 записи, одна с нпп, другая с выгруженным узломссылки, если без 1 всегда будет находить первую запись с нпп и ссылочные реквизиты не загрузятся, с 1 будет найдена последняя строка.
(17) Работает.
Автору +++. Время выгрузки значительно уменьшается, но проблема нехватки памяти к сожалению осталась, при размере в 561 Мб файла выгрузки xml, 1С 7.7 зависает и Windows 10 x64 пишет «Нет свободной оперативной памяти».
(25) попробуйте для ПКО объекта, по которому выгружается наибольшее количество данных поставить «не запоминать выгруженные объекты».
Также, возможно, разбить выгрузку на этапы. Например. 1. справочники, 2. Документы. в ПКО документов указать «не выгружать объекты свойств по ссылкам».
Ну и еще простое решение, разбить выгрузку на порции, например, по видам объектов или периодам.
Спасибо за инфу. Выгружаю только один справочник ТМЦ и зависает (примерно на 200000 записях ), не большие справочники все гут.
Падает в процедуре «ИнициализацияФайлаОбмена» на строчке:
// Добавлено. Начало
ХМЛФайл.WriteLine(УзелПравилаОбмена.xml);
// Добавлено. Конец
Код выше отрабатывает без ошибок:
ХМЛФайл.WriteLine(«ВерсияФормата=» + «»»2.0″»»); //NewNode.xm
Файл создается в UTF-16, я так понимаю, происходит ошибка преобразования символов в кодировке.
Попытался через ADODB.Stream.WriteText с созданием файла в кодировке UTF-8 — тоже самое.
Тестировал на Windows 10, 7, XP.
Кто-то сталкивался с таким? И если да, то как решить проблему?
(28) как мне подсказали: есть ограничение к длине строки.
Опытным путем было установлено что ровно на 500кб уже начинает выгружать корректно, но если запускать помощник перехода на новую редакцию, то все-равно падает с ошибкой (со 100кб — все стало хорошо).
В «ВыгрузитьПоПравилу» необходимо вместо
ХМЛФайл.WriteLine(Приемник.xml);
Вставить:
ДлинаСтроки = СтрДлина(Приемник.xml);
ОграничениеСтроки = 102400; // 100кб
Если ДлинаСтроки > ОграничениеСтроки Тогда
Для Инд = 1 По (Цел(ДлинаСтроки / ОграничениеСтроки) + 1) Цикл
Если ((Инд-1)*ОграничениеСтроки+1) >= ДлинаСтроки Тогда
Продолжить;
КонецЕсли;
ХМЛФайл.Write(Сред(Приемник.xml, ((Инд-1)*ОграничениеСтроки+1),ОграничениеСтроки));
КонецЦикла;
Иначе
ХМЛФайл.WriteLine(Приемник.xml);
КонецЕсли;
В конце «ИнициализацияФайлаОбмена» — тоже самое, только там «УзелПравилаОбмена.xml»