Импорт данных из 1С (dbf) в MySQL
Apr 27, 2013 17:11 · 603 words · 3 minute read
Давным давно писал статью на СвободноХабр про импорт данных из 1С файлов dbf в MySQL, решил перепостить её сюда, т.к., увы, ухожу с того ресурса.
Предыстория
Работали себе люди долго-долго в самописной конфигурации 1С и бед не знали, как открылись у организации филиалы, и директор захотел все данные в одном месте. Сказано — сделано: через пару месяцев родился корпоративный сайт (написанный на php (Yii) + MySQL). Пользователи ринулись добавлять новую информацию, печатать отчёты… в общем, работа закипела. А что ж делать со старыми данными? Правильно, импортировать в новую систему дабы не потерялись. Итак, возникла задача: произвести миграцию из 1С в MySQL. Замечу, что 1С работает на dbf-файлах. Язык программирования для реализации импорта/экспорта был выбран Python. (Я был очень удивлён насколько просто решилась эта задача!).
Структура dbf файлов 1С
Итак, небольшой реверс-инжиниринг. Помимо системных dbf, в каталоге базы находятся и пользовательские:
- SC*.dbf — справочники
- DH*.dbf — документы
- DT*.dbf — табличная часть документов
Главный ключ у справочников — ID, у документов и табличной части — IDDOC. Поля имеют сквозную нумерацию и начинаются с префикса SP. Значения ключей имеют несколько странный вид: 1, 2, 3… 9, A, B… F, G, H… Z, 10, 11… 19, 1A… 1z и т.д., то есть находятся в 36-значной системе счисления.
Реализация
Как говорилось ранее, язык программирования — Python, благо опыт импорта/экспорта уже есть (Oracle, текстовые файлы). Необходимые библиотеки нашлись сразу: dbfpy для работы с dbf и MySQLdb для работы с MySQL. Первая засада ждала меня при создании БД dbf. Способ, описанный в инструкции db = dbf.Dbf(«SC21.dbf») не заработал, небольшое чтение исходников предложило другой
db = dbf.Dbf()
db.OpenFile("SC21.dbf")
Внимание! Все названия полей только в верхнем регистре. Обращаться к полю можно по имени. В случае даты получим тьюпл вида (год, месяц, число). Строковые поля находятся в кодировке ANSI. У библиотеки dbfpy есть также методы для добавления записей, но не будем на этом останавливаться, т.к. это хорошо описано автором в документации.
Из граблей, на которые я наступил:
- Т.к. читаю я из dbf, то надо учитывать, что некоторые записи являются удалёнными. Проверка на актуальность записи в dbfpy выполняется с помощью свойства isDeleted;
- По каким-то причинам ID надо стрипать (rec[“ID”].strip());
Работа с базой MySQL тоже ведётся довольно просто. Приведён пример импорта таблицы “Автомобили”.
from dbfpy import dbf
from types import *
from importCommon import _unicode, recode
import MySQLdb
db = dbf.Dbf()
db.openFile("SC20.DBF")
dbSQL = MySQLdb.connect(host="localhost", user="root", passwd="root", db="AutoSchoolTest", charset='utf8')
cursor = dbSQL.cursor()
sql = "INSERT INTO AutoSchoolTest.Automobile(ID, RegNo, Model) VALUES \n"
for rec in db:
if rec.isDeleted:
continue
sql += "(%s, '%s', '%s'), \n" % (recode(rec["ID"].strip()), _unicode(rec["DESCR"]), _unicode(rec["SP22"]))
sql = sql[:-3]
print sql
cursor.execute(sql)
dbSQL.commit()
dbSQL.close()
Напоследок несколько функций конвертации
from types import *
import datetime
# перекодировка строки из Win1251 в UTF8
def _unicode(str):
str = str.decode('windows-1251').encode('utf-8')
# перекодировка значений ключей
def recode(str):
# преобразование одного символа в число
def recalcChar(char):
charDict = {'1': 1, '2': 2, '0': 0, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15, 'G': 16, 'H': 17, 'I': 18, 'J': 19, 'K': 20, 'L': 21, 'M': 22, 'N': 23, 'O': 24, 'P': 25, 'Q': 26, 'R': 27, 'S': 28, 'T': 29, 'U': 30, 'V': 31, 'W': 32, 'X': 33, 'Y': 34, 'Z': 35}
return charDict[char]
if str == '0':
return 'null'
# перевод из 36-значной в 10-значную систему счисления
pow = 1
result = 0
while str != '':
val = recalcChar(str[-1:])
result += val*pow
pow = pow*36
str = str[:-1]
return result
# преобразование тьюпла даты в дату, понятную MySQL
def _date(d):
if (d[0] == 0) and (d[1] == 0) and (d[2] == 0):
return ''
else:
return datetime.date(d[0], d[1], d[2])