Один из вариантов переноса данных из 1С 7.7 в 1С 8.Х

Перенос данных — это почти всегда индивидуальный подход. И крайне важно выбрать наиболее подходящий инструмент из множества доступных. Я в этой статье опишу один из вариантов.

Итак, речь пойдет о ADODB. Какие же преимущества мы можем получить используя данный вариант обмена? Во-первых — это скорость работы. Ни OLE, ни файловый обмен не дадут той скорости доступа к базам 1С 7.7, как ADODB. Во вторых — универсальность, то есть его можно применять как в 1С 7.7 (там, правда, есть более удобные механизмы 1С++), так и в 1С 8.Х, да и в принципе везде где поддерживается работа с ADODB.

Первое что нам понадобится — это создать подключение к базе 1С (я буду рассматривать вариант dbf).

Соединение = Новый COMObject("ADODB.Connection"); // Создаем соединение
Соединение.ConnectionString = "Provider=VFPOLEDB.1;Data Source="+СокрЛП(Каталог)+";Extended Properties=dBASE IV;CODEPAGE=cp1251;"; //Внимание! CODEPAGE=cp1251; работает почему-то не везде.
Попытка
Соединение.Open();
Исключение
Сообщить(ОписаниеОшибки());
Возврат;
КонецПопытки;
Соединение.Close();

Дальше нам надо создать команду.

Команда = Новый COMObject("ADODB.Command");
Команда.ActiveConnection = Соединение;

И получить данные.

Команда.CommandText = ТекстЗапроса;
РекордСет = Команда.Execute();

Обход полученных данных можно сделать как:

Пока РекордСет.Eof() = 0 Цикл
РекордСет.MoveNext();
КонецЦикла;

Но в любой бочке мёда, есть ложка дёгтя. Если в цикле Выполнить запрос, то наш «рекордсет» заполнится новыми данными. Поэтому я бы рекомендовал после запроса переносить данные в «Таблицу значений». Сделать это можно так:

Функция СоздатьТаблицу(РекордСет)
ТаблицаРезультатаЗапроса = Новый ТаблицаЗначений();
Если РекордСет.Eof() = 0 Тогда
Для ит = 0 по РекордСет.Fields.Count - 1 Цикл
ТаблицаРезультатаЗапроса.Колонки.Добавить(РекордСет.Fields(ит).Name);
КонецЦикла;
Пока РекордСет.Eof() = 0 Цикл
СтрокаТаблицы = ТаблицаРезультатаЗапроса.Добавить();
Для ит = 0 по РекордСет.Fields.Count - 1 Цикл
СтрокаТаблицы.Установить(ит, РекордСет.Fields(ит).Value);
КонецЦикла;
РекордСет.MoveNext();
КонецЦикла;
КонецЕсли;
Возврат ТаблицаРезультатаЗапроса;
КонецФункции

Сложим все вместе и посмотрим, что у нас получилось.

Функция СоздатьТаблицу(РекордСет)
ТаблицаРезультатаЗапроса = Новый ТаблицаЗначений();
Если РекордСет.Eof() = 0 Тогда
Для ит = 0 по РекордСет.Fields.Count - 1 Цикл
ТаблицаРезультатаЗапроса.Колонки.Добавить(РекордСет.Fields(ит).Name);
КонецЦикла;
Пока РекордСет.Eof() = 0 Цикл
СтрокаТаблицы = ТаблицаРезультатаЗапроса.Добавить();
Для ит = 0 по РекордСет.Fields.Count - 1 Цикл
СтрокаТаблицы.Установить(ит, РекордСет.Fields(ит).Value);
КонецЦикла;
РекордСет.MoveNext();
КонецЦикла;
КонецЕсли;
Возврат ТаблицаРезультатаЗапроса;
КонецФункции

Функция ВыполнитьЗапрос(Соединение, ТекстЗапроса)
Команда = Новый COMObject("ADODB.Command");
Команда.ActiveConnection = Соединение;
Команда.CommandText = ТекстЗапроса;
РекордСет = Команда.Execute();
Возврат РекордСет;
КонецФункции

Процедура ЗагрузитьСправочник()
Соединение = Новый COMObject("ADODB.Connection"); // Создаем соединение

Соединение.ConnectionString = "Provider=VFPOLEDB.1;Data Source="+СокрЛП(Каталог)+";Extended Properties=dBASE IV;CODEPAGE=cp1251;"; // Внимание! CODEPAGE=cp1251; работает почему-то не везде.

Попытка
Соединение.Open();
Исключение
Сообщить(ОписаниеОшибки());
Возврат;
КонецПопытки;

ТекстЗапроса = "SELECT SC192.CODE AS Код
|      , SC192.ID AS ID
|      , SC192.PARENTID AS ГруппаНаименование
|      , SC192.ISFOLDER AS ЭтоГруппа
|      , SC192.DESCR AS Наименование
|      , SC192.SP1817 AS МодельАвто
|      , SC192.SP194 AS ПолноеНаименовани
|      , SC192.SP195 AS ЮридическийАдрес
|      , SC192.SP196 AS ПочтовыйАдрес
|      , SC192.SP197 AS Телефоны
|      , SC192.SP198 AS ИНН
|      , SC192.SP199 AS ДокументСерия
|      , SC192.SP200 AS ДокументНомер
|      , SC192.SP201 AS ДокументДатаВыдач
|      , SC192.SP202 AS ДокументКемВыдан
|      , V1.CODE AS ВалютаВзаиморасче
|      , SC192.SP210 AS НомерКарты
|      , SC192.SP211 AS ГосНомер
|      , SC192.SP1796 AS Скидка
|      , SC192.SP1829 AS КПП
| FROM SC192
| LEFT JOIN
| SC95 AS V1 ON
| SC192.SP203 = V1.ID
| "+?(ЧастичнаяЗагрузка,"RIGHT JOIN exchange ON SC192.ID = exchange.ID AND exchange.VID = 'Контрагенты'","")+"
|";

ТаблицаРезультатаЗапроса = СоздатьТаблицу (ВыполнитьЗапрос(Соединение, ТекстЗапроса));

Для Каждого СтрокаТЧ ИЗ ТаблицаРезультатаЗапроса Цикл
...
Объект.Наименование = СтрокаТЧ.Наименование;
Объект.Код = СтрокаТЧ.ID;
...
КонецЦикла;
Соединение.Close();
КонецПроцедуры

P.S. Данный метод дает и ещё одно преимущество над остальными — это уникальный идентификатор объекта в базе 1С 7.7 («ИмяТаблицы.ID»).

 

P.P.S. Данная статья не ставила целью провести обзор T-SQL или структуры данных 1С. Все это можно найти здесь:

http://www.sql.ru/docs/mssql/tsql_ref/index.shtml

http://www.script-coding.info/v77tables.html

и т.д.

Эпилог.  В 2005 MS SQL появилась прекрасное дополнение — возможность в предложении FOR XML использовать директиву TYPE. Давайте перепишем запрос, что бы убрать лишние действия.

ТекстЗапроса = "
| SELECT (SELECT SC192.CODE AS Код
|      , SC192.ID AS ID
|      , SC192.PARENTID AS ГруппаНаименование
|      , SC192.ISFOLDER AS ЭтоГруппа
|      , SC192.DESCR AS Наименование
|      , SC192.SP1817 AS МодельАвто
|      , SC192.SP194 AS ПолноеНаименовани
|      , SC192.SP195 AS ЮридическийАдрес
|      , SC192.SP196 AS ПочтовыйАдрес
|      , SC192.SP197 AS Телефоны
|      , SC192.SP198 AS ИНН
|      , SC192.SP199 AS ДокументСерия
|      , SC192.SP200 AS ДокументНомер
|      , SC192.SP201 AS ДокументДатаВыдач
|      , SC192.SP202 AS ДокументКемВыдан
|      , V1.CODE AS ВалютаВзаиморасче
|      , SC192.SP210 AS НомерКарты
|      , SC192.SP211 AS ГосНомер
|      , SC192.SP1796 AS Скидка
|      , SC192.SP1829 AS КПП
| FOR XML TYPE, PATCH('CatalogeObject.Номенклатура') ) AS A
| FROM SC192
| LEFT JOIN
| SC95 AS V1 ON
| SC192.SP203 = V1.ID
| "+?(ЧастичнаяЗагрузка,"RIGHT JOIN exchange ON SC192.ID = exchange.ID AND exchange.VID = 'Контрагенты'","")+"
|";

Теперь в 1С достаточно будет следующего кода:

Пока РекордСет.Eof() = 0 Цикл
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку(РекордСет.Fields(0).Value);
ЗаписьXML.ПерейтиКСодержимому();
Объект = ПрочитатьXML(ЗаписьXML);
Объект.Записать();
ЧтениеXML.Закрыть();
РекордСет.MoveNext();
КонецЦикла

28 Comments

  1. Serj1C

    Может скорость доступа и выше, но после того как мы прочитали таблицы запросом, эти данные нужно обработать… а они сосвем в не нечитабельном виде… Непонятные имена колонок, непонятные ссылки… Дальше то что?

    Reply
  2. artbear

    Пока минусую, т.к. публикация не показывает проблемы, указанные, например, в (1)

    Reply
  3. awk

    Serj1C — правда? У меня читабельны…

    ТекстЗапроса = «SELECT SC192.CODE AS Код
    |  , SC192.ID AS ID
    |  , SC192.PARENTID AS ГруппаНаименование
    |  , SC192.ISFOLDER AS ЭтоГруппа
    |  , SC192.DESCR AS Наименование
    |  , SC192.SP1817 AS МодельАвто
    |  , SC192.SP194 AS ПолноеНаименовани
    |  , SC192.SP195 AS ЮридическийАдрес
    |  , SC192.SP196 AS ПочтовыйАдрес
    |  , SC192.SP197 AS Телефоны
    |  , SC192.SP198 AS ИНН
    |  , SC192.SP199 AS ДокументСерия
    |  , SC192.SP200 AS ДокументНомер
    |  , SC192.SP201 AS ДокументДатаВыдач
    |  , SC192.SP202 AS ДокументКемВыдан
    |  , V1.CODE AS ВалютаВзаиморасче
    |  , SC192.SP210 AS НомерКарты
    |  , SC192.SP211 AS ГосНомер
    |  , SC192.SP1796 AS Скидка
    |  , SC192.SP1829 AS КПП
    | FROM SC192
    | LEFT JOIN
    | SC95 AS V1 ON
    | SC192.SP203 = V1.ID
    | «+?(ЧастичнаяЗагрузка,»RIGHT JOIN exchange ON SC192.ID = exchange.ID AND exchange.VID = ‘Контрагенты'»,»»)+»
    |»;
    ТаблицаРезультатаЗапроса = СоздатьТаблицу (ВыполнитьЗапрос(Соединение, ТекстЗапроса));
    Для Каждого СтрокаТЧ ИЗ ТаблицаРезультатаЗапроса Цикл
    …
    Объект.Наименование = СтрокаТЧ.Наименование;
    Объект.Код = СтрокаТЧ.ID;
    …
    КонецЦикла;
    

    Показать

    Reply
  4. Serj1C

    (3) > Перенос данных — это почти всегда индивидуальный подход

    Иммет ли смысл ради одного переноса на столько глубоко ковырятся в физической структуре ИБ? Я бы предпочел использовать типовую КД.

    Разработка быстрее, отладка проще.

    По-моему время выгрузки стоит дешевле, чем время разработки.

    Reply
  5. awk

    Я не настаиваю.. Просто, когда позиций номенклатуры 38 000, а аналогов 108 000. Выгрузка худо-бедно идёт за часа два. А загрузка — восемь. И того 10 часов (при режиме работы 24/7 — это не приемлемая цена). ADODB — 40 минут (без оптимизации на dbf, с оптимизацией, да на MSSQL минут десять будет работать). При этом встает вопрос о реквизите синхронизации. Код? Наименование? И то и другое пользователи легко могут изменить. А поскольку я привык смотреть на структуру баз 1с 7.7 — то сложности особо не вижу (скорость написания запроса аля в статье ну раза в два в три дольше чем КД). А отладка в моём варианте проще, так как происходит в конфигураторе..

    Reply
  6. Serj1C

    (5) Я тоже не настаиваю.

    Допустим, на выгрузке экономия 1 час 50 минут.

    Теперь загрузка. 8 часов. Как ее оптимизировать?

    Reply
  7. awk

    40 минут это вместе с загрузкой…

    Reply
  8. ACE$

    интересно, да… структуру можно посмотреть в соответствующих файлах. Хотя разбираться в ней придется довольно долго. За идею +

    Reply
  9. artbear

    (0) Тема все-таки до конца не раскрыта.

    Например, не указал, где брать названия таблиц, полей, индексов? Понятно, что в дд-файле, но не все это знают 🙂

    Периодические реквизиты и длинные строки так не выгрузишь 🙁

    и т.д. и т.п.

    Reply
  10. afanasko

    Объясняю ситуацию:

    вот есть реквизит «ВалютаВзаиморасчетов». Это ссылка на элемент справочника. У неё может быть родитель, у родителя может быть свой родитель, владелец или реквизит также ссылающийся на элемент какого-то справочника. А если тип реквизита — перечисление?

    Как с помощью данного варианта можно рекурсивно обработать все ссылки загружаемых элементов?

    Второй вопрос: а для каждой базы необходимо переписывать имена таблиц и столбцов? Или есть какой-то способ абстрагироваться от физического хранения?

    Такой способ можно использовать для единичной загрузки из 1С в стороннюю учетную систему, но для обмена между двумя 1С ничего лучше чем ОЛЕ, при всех его недостатках, еще не придумали. Поэтому не изобретайте велосипеды, ИМХО.

    Минус.

    Reply
  11. Serj1C

    (7) Поясните за счет чего.

    8 часов и 40 минут… Нас кто-то вводит в заблуждение

    Reply
  12. awk

    Re: Тема все-таки до конца не раскрыта.

    Для того кто не знает где взять структуру — надо читать не эту статью, а статьи на сайте http://script-coding.info/. Переодические реквизиты и длинные строки выгрузить вполне реально, это то же тема отдельной статьи

    Re: 8

    Не долго поверьте…

    Re: 10

    Первый вопрос — http://script-coding.info/. Без знания структуры данных — не отвечу. Нужна конкретная задача.

    Второй вопрос — переписывать не надо, а дать псевдонимы полей, для удобства себя любимого, можно.

    Re: 11

    К сожалению не ввожу.. XML вещь прекрасная, но для хранения больших объемов данных не подходящая. Не восемь, а 10 часов против сорока минут. 1С бездарно использует хранилища информации — это не секрет. Для DBF варианта — это ещё терпимо, но когда она перебирается на SQL сервера… Скорость достигается, за счет отсутствия поиска и конвертации объектов методами 1С (кроме НайтиПо…).

    P.S. Ругаетесь, но никто не написал неинтиресно — это приятно.

    Reply
  13. artbear

    (12) Так я тебе и намекал, а сейчас прямо говорю:

    добавь в описание ссылки на скрипт-кодинг, которые раскрывают то, что ты не указал.

    И будет замечательная, законченная публикация 🙂

    (11) Цифры вполне реальные

    Reply
  14. xpym-xpym

    по поводу (10) (12) и (3)

    Второй вопрос: а для каждой базы необходимо переписывать имена таблиц и столбцов? Или есть какой-то способ абстрагироваться от физического хранения?

    Без знания структуры данных — не отвечу.

    интересно сколько времени займет именно подготовка для не типовой или правленой конфы для приведения в читабельный вид

    з.ы. автор статью дополнит я надеюсь

    Reply
  15. awk

    1. Не дополню. Статья закончена. Могу только новую написать вопрос какая тема.

    2.

    Первый вопрос — http://script-coding.info/. Без знания структуры данных — не отвечу. Нужна конкретная задача.

    А ответ на второй вопрос:

    Второй вопрос — переписывать не надо, а дать псевдонимы полей, для удобства себя любимого, можно.

    Читабельность определяется квалификацией. F = m*a — читабельно? А :

    Д | K

    _____

    50|62

    Reply
  16. Serj1C

    Парсер DD ))

    Уже прикольно

    Reply
  17. awk

    Парсеров этих… Много, если одним словом. У меня на C# мной написаный где-то дома валяется… Этот сегодня написал, как внешний отчет для 8.2. Если кто подскажет как формируется _IDRRef можно и простенькую конвертацию написать…

    Reply
  18. osokolov

    А зачем вот так-то, через …опу ?

    Выбрать данные из 7.7 стандартными средствами — не проблема, гораздо больше времени уйдет на создание объектов в базе-приемнике и проведении документов в ней.

    Reply
  19. awk

    1. Обмен через OLE не плохой вариант.

    Плюсы:

    Простота реализации.

    Минусы:

    Долго подключается (версия 7.7).

    Проблемы с передачей параметров.

    Нужно иметь учетную запись и пароль (если не ломать).

    Базы должны быть связаны сетью хотя бы 10 Мб/с.

    Итог: Очень хорошо подходит для обмена внутри локальной сети.

    2. Файловый обмен.

    Плюсы:

    Не зависит от расположения баз.

    Минусы:

    Обмен состоит из двух частей (выгрузка, загрузка). Для каждой надо писать обработку, с помощью конвертации данных или без нее.

    Парсеры файлов не блещут скоростью работы.

    Итого: Подходит для обмена если базы разделены медленным каналом или таковой отсутствует.

    3. Так называемые «прямые запросы».

    Плюсы:

    Скорость работы не сравнима с первыми двумя (выше на порядок).

    При должном опыте реализация достаточно проста. Не сложнее конвертации или OLE.

    Доступна на медленных каналах (требует настройки доступа если происходит через интернет).

    Минусы:

    Требует знания T-SQL, структуры базы 1С.

    Итого: Если не уверены в своих силах то: НИКОГДА НЕ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ НЕ ИСПОЛЬЗОВАТЬ!!! В противном случае идеальный вариант во многих случаях.

    Общие итоги: Что нужно в конкретном случае определяется конкретной задачей. Где-то выгоден OLE, где-то файловый обмен. А где-то без «прямых запросов» не обойтись (если не отказываться от решения задачи).

    Reply
  20. Bosma

    (19)….Нужно иметь учетную запись и пароль (если не ломать).

    странное замечание…

    что за обмен, если и учетных данных не знаем? похоже на воровство

    Reply
  21. awk

    (20) Сто рублей валяется на дороге. Ты шел мимо, нагнулся, поднял — это воровство или находка?

    Reply
  22. artbear

    (0) Минус снял.

    Reply
  23. Ветер в поле

    Подумав, решил, не писать 🙂

    Reply
  24. nicxxx

    побуду некропостером 🙂

    (0) если ADO совместить с УРБД (v8), то 40 минут легко превращаются в 5 даже для DBF-базы. смысл в том, чтобы сформировать xml-файл обмена для 1С 8 по данным из 1С 7.7

    Reply
  25. awk

    (24) nicxxx, Я немного развил и использовал SELECT … FOR XML … в итоге получил готовый XML для загрузки.

    Reply
  26. shoy

    опробовал и обрадовался 😉

    такой подход понравился! плюсую

    Reply
  27. Jogeedae

    Добрый день! у меня чего-то не взлетело 🙁

    Вот запрос:

    SELECT
    _IDRRef AS ID,
    _Code AS Код,
    _Description AS Наименование
    FROM _Reference34
    FOR XML AUTO,
    binary base64 —использую эту директиву чтобы не ругалось

    Получаю результат:

    <_Reference34 ID=»mHw/dfd7diZIb99hmH6Fqw==» Код=»00000″ Наименование=»Федеральный» />
    <_Reference34 ID=»pnWd7NphzC1DPrBdYHGRGQ==» Код=»00000″ Наименование=»Муниципальный» />
    <_Reference34 ID=»vkwlYLrOXuNJjUN1w8XLkg==» Код=»00000″ Наименование=»Региональный» />

    это нормально: ID=»mHw/dfd7diZIb99hmH6Fqw==» или можно было получить ссылку в виде «0x…» или в каком-либо более удобном виде?

    Десериализация этих ссылок как должна происходить? Совсем непонятно.

    Нужно описание объектов в XDTO готовить?

    Надеюсь на помощь! Заранее спасибо!

    Reply
  28. awk

    (27) Jogeedae,

    1. _IDRRef имеет тип binary(16), то есть двоичные данные. Его для начала надо бы в строку 36 преобразовать. Под рукой скрипта нет, но гугль помнить должен.

    2. «binary base64 —использую эту директиву чтобы не ругалось» что бы не ругалось, надо понять на что ругается, а не ставить заглушки.

    3. Если вы хотите стандартизировать объект и сделать его неизменным от времени и базы, то да. А если готовы подгонять запросы под, то нет.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *