Один из вариантов переноса данных из 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 не будет опубликован. Обязательные поля помечены *