Имя: Пароль:
1C
1С v8
Быстрый перенос данных с ГУИДами
, ,
0 Lepochkin
 
30.11.17
17:28
Всем доброго времени суток.
Есть задача по переносу данных из 1с в sql. Причем в качестве объектов передавать не их человеко читабельные представления в виде кода или наименования а ГУИД объекта.

Если последовательно перебирать выборку и на каждом объекте применять метод XML строка, то получается безумно долго.
Докопался до метода с сериализацией таблицы значений вот таким способом

Функция СериализоватьДанные(мБаза,СтрокиТаблицы)
    #Если Клиент Тогда
        Состояние("Сериализация... ");
    #КонецЕсли    
        
    ЗаписьXML = мБаза.NewObject("ЗаписьXML");
    ЗаписьXML.УстановитьСтроку();
    
    ОбъектXDTO = мБаза.СериализаторXDTO.ЗаписатьXDTO(СтрокиТаблицы);
    мБаза.ФабрикаXDTO.ЗаписатьXML(ЗаписьXML,ОбъектXDTO);
    ХранилищеВСтроке = ЗаписьXML.Закрыть();

    ТекстДок = Новый ТекстовыйДокумент;
    ТекстДок.УстановитьТекст(ХранилищеВСтроке);
    
    ТекстДокНовый = Новый ТекстовыйДокумент;
    
    СтрокаXML = "<Type>xs:string</Type>";
    СтрокаТипXML = "<Value xsi:type="+"""xs:string""";
    Для н = 1 По ТекстДок.КоличествоСтрок() Цикл
        стр = ТекстДок.ПолучитьСтроку(н);
        Если н > 1 и Найти(стр,"http://v8.1c.ru/8.1/data/enterprise/current-config";) > 0 И Найти(стр,"Value") > 0 Тогда
            СимволЗакрыти = Найти(Стр,">");
            НоваяСтрока = СтрокаТипXML + сред(Стр,СимволЗакрыти);
            Стр = НоваяСтрока + Символы.ПС;    
        ИначеЕсли н > 1 и Найти(стр,"http://v8.1c.ru/8.1/data/enterprise/current-config";) > 0 Тогда
            Стр = СтрокаXML + Символы.ПС;    
        КонецЕсли;    
        
        ТекстДокНовый.ДобавитьСтроку(Стр);
    КонецЦикла;
    
    ХранилищеПереработанное = ТекстДокНовый.ПолучитьТекст();
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(ХранилищеПереработанное);
    ТЗ = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
    возврат ТЗ;
КонецФункции

Работает в разы быстрее, но опытным путем установлено, что там нормально работает только на таблицах, в которых содержится 1000 строк или меньше. Потому приходится обходить выборку и формировать маленькие таблички по 1000 строк. Этот метод все равно быстрее, чем первый примерно в 8 раз. В минуту грузим 10000 записей.

Замер производительность показывает, что 60 времени уходить на операцию заполнения значений строки таблицы значений
   НСтр = СтрокиТаблицы.Добавить();
   ЗаполнитьЗначенияСвойств(НСтр,Выборка);

Хотелось бы еще ускорить процесс но уже не знаю куда копнуть.
Подскажите какие еще могут быть варианты быстрого получения ГУИДов или какой-нибудь хитрый способ разбиения результатов запроса на несколько маленьких табличек. Вариант выгрузить в таблицу значений все, потом пронумеровать и делать выборку из нее по частям исходя из номера пробовал. Выходит дольше.

Хочу скорости )) Предложите плиз какие-нибудь варианты
1 Ёпрст
 
30.11.17
17:32
(0) а чего, взять гуид объекта сразу с таблички в скуле и переписать куда надо, не вариант ?
2 Lepochkin
 
30.11.17
17:33
Я бы взял, да найти не могу...
3 Ёпрст
 
30.11.17
17:33
т.е берешь _IDRRef из таблички _Document*** и инсертишь куды те там надо
4 Lepochkin
 
30.11.17
17:33
Он же ведь там в другом виде хранится
5 Ёпрст
 
30.11.17
17:34
(2) файловая что ле ?
6 Ёпрст
 
30.11.17
17:34
(4) в том же самом и хранится, в виде гуида
7 Lepochkin
 
30.11.17
17:34
Нее... Скуль
8 Ёпрст
 
30.11.17
17:34
просто в 1с "чутка" преобразован
9 Lepochkin
 
30.11.17
17:36
мне нужно ваот так - 00072dd6-f326-11e4-8936-e4115be3c6a9
а в скуле вот так - 0x0000FE44C60311E3964FE4115BE3C6A9
10 Ёпрст
 
30.11.17
17:37
Так что, 1с тут вообще не нужен, ну разве что запрос по ado там запустить
11 Ёпрст
 
30.11.17
17:37
(9) и че ?
:)
12 Вафель
 
30.11.17
17:37
тебе в строку гуид нужно преобразовать?
13 Lepochkin
 
30.11.17
17:37
Я понимаю, что преобразован. И даже знаю как из одного другой получить, только для этого выборку обходит надо будет опять. И по скорости проигрышь будет
14 Ёпрст
 
30.11.17
17:37
преобразовать гуид в уид можно же, в скуле тупо хранимкой
15 Ёпрст
 
30.11.17
17:38
(13) ?
16 Lepochkin
 
30.11.17
17:42
Хранимка штука конечно интересная, но боюсь моем случае не подойтет.
Мне нужно с разных конфигураций данные собирать, причем где-то в чистом виде, где-то результаты запросов для отчетов... И под каждую базу еще и преобразования делать... Тяжеловато будет
17 Ёпрст
 
30.11.17
17:43
(16) Ну, тогда выбирай - тебе либо шашечки, либо ехать.
18 Lepochkin
 
30.11.17
17:48
Если "ехать" - то при добавлении любого объекта мне потрудиться не кисло надо будет...
Если "шашечки" - то я отдал инструмент и забыл...
Мы ж ленивые люди...
Но хочется что бы побыстрее было ))
19 Ёпрст
 
30.11.17
17:50
(18) Что именно вкладывается в понятие "потрудится" ? Ты же имена таблиц и  полей, не руками будешь добавлять в запрос.
20 Lepochkin
 
30.11.17
17:57
Что-то плохо у меня картинка складывается...
Мне получается надо будет запрос 1ски конвертнуть в скулевый. Его там исполнить. Обойти результаты скулевого, преобразовать уид и записать в другую базу...
21 Ёпрст
 
30.11.17
17:59
нет, нужно просто выполнить sql запрос который сделает insert into select
22 Lepochkin
 
30.11.17
18:09
сейчас это выглядит вот так. http://joxi.ru/ZrJzpoDC1gKxVr
и для пользователя, хоть он и программис хочется что бы так же и оставалось
23 Вафель
 
30.11.17
18:15
если через скд делать, то можно напрямую ссылки в гуиды переводить
24 Вафель
 
30.11.17
18:17
25 Lepochkin
 
30.11.17
21:32
СКД не прокатит... Рассматривал этот вариант. Нужен универсальный механизм, что бы им дергать из любой базы 1с. А им я получу com объект
26 Tateossian
 
30.11.17
21:47
Смотри, перечисляю все доступные методы решения этой задачи:
Вариант1 = ЧислоИзШестнадцатеричнойСтроки("0xabcde");

Вариант2 - используя функцию Новый БуферДвоичныхДанных и сложение по модулю И

Вариант3 = VBScript.RegExp

Вариант4 = самодельный парсер строк.

Выбирай любой)))
27 Lepochkin
 
30.11.17
22:41
Вариант4 - это наверное то, что я уже изобразил...
1 и 3 вряд ли будут универсальны.
а вот 2 кажется интересным. завтра посмотрю в его сторону
28 Tateossian
 
01.12.17
03:24
(27) Вариант 4 ужасный колхоз.

Пример с вариантом (3 и 2) смотри ниже (он универсален), так как guid формируется по RFC4122 (https://tools.ietf.org/html/rfc4122)

Как оказалось, платформа не может в сдвиг. Пришлось немного подшаманить. В итоге получилось вот так (тут сразу с замером производительности. Сей код (10000 итераций) на толстяке выполняется 1400 мс.

СтрокаГекс = "0123456789ABCDEF";
ГСЧ = Новый ГенераторСлучайныхЧисел;

Старт = ТекущаяУниверсальнаяДатаВМиллисекундах();

Для i = 0 По 9999 Цикл
    
    ГексСтрингУИД="";
    Для j=0 По 31 Цикл
        Рандом = ГСЧ.СлучайноеЧисло(0, 15);
        ГексСтрингУИД=ГексСтрингУИД+Сред(СтрокаГекс, Рандом, 1);    
    КонецЦикла;

    ПБ = ПорядокБайтов.BigEndian;

    УИДБуфер = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ГексСтрингУИД);
    УИДБуфер.ПорядокБайтов = ПБ;

    СтаршиеБиты = УИДБуфер.Прочитать(0, 8); //fst
    МладшиеБиты = УИДБуфер.Прочитать(8, 8); //snd

    Маска5 = Новый БуферДвоичныхДанных(8, ПБ);
    Маска5.ЗаписатьЦелое64(0, 4294967295, ПБ);
    Группа5 = МладшиеБиты.Скопировать();
    Группа5.ЗаписатьПобитовоеИ(0, Маска5);
    СдвинутыйБуфер5 = Новый БуферДвоичныхДанных(16, ПБ); // <<4
    СдвинутыйБуфер5.Записать(4, Группа5);
    СдвинутыйБуфер5 = СдвинутыйБуфер5.Прочитать(8, 8);

    Маска4 = Новый БуферДвоичныхДанных(8, ПБ);
    Маска4.ЗаписатьЦелое64(0, ЧислоИзШестнадцатеричнойСтроки("0xFFFF00000000"), ПБ);
    Группа4 = МладшиеБиты.Скопировать();
    Группа4.ЗаписатьПобитовоеИ(0, Маска4);
    СдвинутыйБуфер4 = Новый БуферДвоичныхДанных(16, ПБ); // >>2
    СдвинутыйБуфер4.Записать(10, Группа4);
    СдвинутыйБуфер4 = СдвинутыйБуфер4.Прочитать(8, 8);

    Маска3 = Новый БуферДвоичныхДанных(8, ПБ);
    Маска3.ЗаписатьЦелое64(0, ЧислоИзШестнадцатеричнойСтроки("0xFFFF000000000000"), ПБ);
    Группа3 = МладшиеБиты.Скопировать();
    Группа3.ЗаписатьПобитовоеИ(0, Маска3);
    СдвинутыйБуфер3 = Новый БуферДвоичныхДанных(16, ПБ); // >>6
    СдвинутыйБуфер3.Записать(14, Группа3);
    СдвинутыйБуфер3 = СдвинутыйБуфер3.Прочитать(8, 8);

    НоваяСтаршаяГруппа = Новый БуферДвоичныхДанных(8, ПБ);
    НоваяСтаршаяГруппа.ЗаписатьПобитовоеИли(0, СдвинутыйБуфер3);
    НоваяСтаршаяГруппа.ЗаписатьПобитовоеИли(0, СдвинутыйБуфер4);
    НоваяСтаршаяГруппа.ЗаписатьПобитовоеИли(0, СдвинутыйБуфер5);

    ПолныйБуфер = Новый БуферДвоичныхДанных(16, ПБ);
    ПолныйБуфер.Записать(0, НоваяСтаршаяГруппа);
    ПолныйБуфер.Записать(8, СтаршиеБиты);

    // 4-2-2-2-6
    //new UUID("00072dd6-f326-11e4-8936-e4115be3c6a9")

    ПравильныйУИД = НРег(
                    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(0,4)) + "-" +
                    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(4,2)) + "-" +
                    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(6,2)) + "-" +
                    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(8,2)) + "-" +
                    ПолучитьHexСтрокуИзБуфераДвоичныхДанных(ПолныйБуфер.Прочитать(10,6)));

КонецЦикла;

//

Длительность = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт;


Что касается твоего замечания насчет "Работает в разы быстрее, но опытным путем установлено, что там нормально работает только на таблицах, в которых содержится 1000 строк или меньше" то тут все просто - сериализатор использует DOM-анализатор, а это значит, что весь XML загружается в оперативную память, а, учитывая то, что у тебя код написан с утечками памяти (хотя бы то, что ты не закрываешь xml-дескрипторы), получаешь обозначенные ограничения. В любом случае, в твоем конкретном примере использование XPath будет куда более эффективнее.
29 Lepochkin
 
01.12.17
10:34
(28) Не очень понял как этот код применим для меня.
Объясните на пальцах плиз. Есть выборка из базы 1с, к которой я подключен com соединениям. Выборка следующая

Выборка.Ссылка - com объект (здесь должен быть ГУИД)
Выборка.Наименование - строка
Выборка.Код - Число

Данные по этой выборке я должен вставить в sql базу.
30 Tateossian
 
01.12.17
10:40
(29)  Ты о чем спрашиваешь в посте?
31 Lepochkin
 
01.12.17
10:44
Изначальная задача. Есть N баз 1с, нужно создать универсальный инструмент по выгрузке данных из них в базу SQL.
Далее я описывал свое решение в поисках его оптимизации
32 Вафель
 
01.12.17
10:45
скд можно и в чужой базе выполнить
33 Вафель
 
01.12.17
10:46
схему собирать ручками
34 Вафель
 
01.12.17
10:46
Большой плюс будет, что не нужно будет ссылки возвращать, а сразу строки
35 Tateossian
 
01.12.17
10:52
(29) Вот это ты к чему написал?
«мне нужно ваот так - 00072dd6-f326-11e4-8936-e4115be3c6a9
а в скуле вот так - 0x0000FE44C60311E3964FE4115BE3C6A

Напиши, пример guida и его xml-представления 1С любого справочника.
36 Вафель
 
01.12.17
10:53
(35) Тут вопрос: как из языка 1с перевести в язык скл? свой парсер писать. Слишком сложная задача
37 Lepochkin
 
01.12.17
10:54
(35) Это была часть обсуждения. Задачу я уже озвучил.
(33) Схему раками пособирать - неплохая идея... Попробую
38 Ц_У
 
01.12.17
10:55
(37) раками не надо, итак все через них.. :)
39 Tateossian
 
01.12.17
10:58
(37) Вы пример не прислали, какой - я написал.
40 Tateossian
 
01.12.17
11:02
Зачем вы храните во внешней базе ссылку как nchar(36)? Если это тип binary(16)?
41 Ёпрст
 
01.12.17
11:03
(31) как ты данные из 1с тащишь ?
42 Tateossian
 
01.12.17
11:05
(36)  Это не сложно, но, думаю, ОПу это не поможет.
43 Ёпрст
 
01.12.17
11:05
не понимаю, в чем у тебя сложность сразу вытащить sql запросом данные из 1с и инсёртить их в твою табличку.
Так же, динамически собираешь текст запроса и привет
44 Lepochkin
 
01.12.17
11:32
(40) У меня помимо баз 1с есть еще и wms система, откуда тоже данные тащить надо, а ключами по той же номенклатуре являются строковые ГУИДы... Потому вот такая и заморочка
45 Lepochkin
 
01.12.17
11:33
Под ГУИДом я понимаю уникальный идентификатор вот такого вида 00072dd6-f326-11e4-8936-e4115be3c6a9
46 Tateossian
 
01.12.17
12:08
(45) Тогда понятно. Колхоз, в общем.
47 Tateossian
 
01.12.17
12:12
(45) Я бы рассказал про представления (VIEW) и синонимы (SYNONIM). Но порекомендую почитать книжки по MS SQL или не трогать сервер.
48 Lepochkin
 
01.12.17
12:26
(47) Почитать - это безусловно беспроигрышное предложение
49 FIXXXL
 
01.12.17
12:32
(0) сделать общий реквизит "ГУИД" и один раз его заполнить по всем базам? плюс подписка на новые ссылочные элементы ПриЗаписи
50 Lepochkin
 
01.12.17
12:39
(49) Думал над этим вариантом, но тогда придется вносить изменения в конфигурации, а хочется сделать без этого
51 Tateossian
 
01.12.17
12:52
(48) Разумеется, еще и разобраться. Задумайся: почему в 1С ссылки хранятся как binary(16) в 1С, а в вашей там какой-то WMS (?) системе как строки. Они, поди, хоть в индексе?

Если бы ты знал, что такое view, то знал бы, что в sql можно сделать хоть сколько угодно view, а в 1С это подключить как внешний источник. А еще тот код, что я тебе написал выше, прекрасно работает в sql и поместится в одну строчку как скалярный оператор - надо его только правильно переписать.
52 Lepochkin
 
01.12.17
14:22
(49) Ты не поверишь, но вьюхи использую в полный рост. И как внешние источники и в отчетах. Причем вьюхи на скулевые и орокловые. Так что сильно не выделвайся. И почему ключи не строковые тоже все студенты знают. Строковый ГУИД используется при интеграции и от него мне не убежать. Вот и думаю как все сделать оптимально
53 Вафель
 
01.12.17
15:22
(51) предлагаешь выкинуть эту wms и написать свою?
54 Lepochkin
 
01.12.17
15:41
Подтупливаю что-то в пятницу вечером.
Вот это выражение Тип("НаборДанныхЗапросСхемыКомпоновкиДанных") через com как получить?
55 Lepochkin
 
01.12.17
15:42
Схему легко...
СхемаКомпановки = мБаза.NewObject("СхемаКомпоновкиДанных");
А тип что-то туплю...
56 Lepochkin
 
01.12.17
17:42
Для истории
Функция ВернутьТипВнешнейБазы(мБаза,СтрокаТипа)
    Описатель = мБаза.NewObject("ОписаниеТипов",СтрокаТипа);
    возврат Описатель.Типы().Получить(0);
КонецФункции
57 Адинэснег
 
04.12.17
07:49
а чем УникальныйИдентификатор() плох?
13500 вызовов за 0,14 сек:
https://image.prntscr.com/image/AY0PU48lTYGhVmW-tkq4iQ.png
Процедура ПолучитьТаблицуУИД()
    
    Запрос = Новый Запрос("ВЫБРАТЬ
                          |    РеализацияТоваровУслуг.Ссылка КАК Док,
                          |    РеализацияТоваровУслуг.СуммаДокумента,
                          |    РеализацияТоваровУслуг.Номер
                          |ИЗ
                          |    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг");
    Таб = Запрос.Выполнить().Выгрузить();
    Таб.Колонки.Добавить("UID");
    Для Каждого Стр Из Таб Цикл
        Стр.UID = Строка(Стр.Док.УникальныйИдентификатор());
    КонецЦикла;    
    
КонецПроцедуры
Независимо от того, куда вы едете — это в гору и против ветра!