Недавно понадобилось перелить большие объемы данных из БД Oracle в MySQL для последующего анализа. Задача решалась с помощью Python.
Строился sql запрос к таблицам Oracle для выгрузки данных в курсор, и затем, построчно пробегая по курсору, данные заливались в MySQL.
Подготовка модулей pymysql, sshtunnel и cx_Oracle
Для работы с MySQL понадобилось установить модуль pymysql, и модуль sshtunnel, так как подключение находилось за SSH. Для работы с Oracle был установлен модуль cx_Oracle и скачан Oracle Instant Client с сайта Oracle (может понадобиться указать путь к распакованному архиву в переменной среды PATH и перезагрузить компьютер). Так же необходимо иметь актуальные версии Microsoft Visual C++ Redistributable (в моем случае версии 2013).
Устанавливаем модули (через cmd от имени администратора):
pip3 install pymysql
pip3 install sshtunnel
pip3 install cx_Oracle
Переливаем данные из Oracle в MySQL
Работаем со следующим кодом:
import cx_Oracle import pymysql import datetime import os import sshtunnel #Если работаем с ОС Windows, то задаем переменную NLS_LANG = .AL32UTF8 #чтобы избежать проблем с кириллицей if(os.name == 'nt'): os.environ['NLS_LANG'] = '.AL32UTF8' #Объявляем переменные типа даты для последующего цикла (строка 51) startdate = datetime.datetime.now().date() - datetime.timedelta(days=2) enddate = datetime.datetime.now().date() - datetime.timedelta(days=1) insert_query = ("replace into resultTable " "(name, date, mainData, count, sumvalues) " "values (%s, %s, %s, %s, %s)") #Создаем подключение к MySQL и инициализируем объект курсора db = pymysql.connect(passwd="pass11",db="data",host="serv3", user="root") cur = db.cursor() #Создаем подключение к Oracle и инициализируем объект курсора oracleDb = cx_Oracle.connect("username/password@10.250.100.100/servicename") oraclesCursor = oracleDb.cursor() ora_query=''' select * from oracleData where date between to_date('{0} 00:00:00','dd.mm.yyyy hh24:mi:ss') and to_date('{0} 23:59:59','dd.mm.yyyy hh24:mi:ss') ''' #Строим SSH туннель #Здесь пробрасываем порт 12140 #Можно пробросить любой свободный with sshtunnel.SSHTunnelForwarder( ('ssh_address', 22), ssh_username='ssh_username', ssh_password='ssh_password', remote_bind_address=('mysql_address', 3306), local_bind_address=('127.0.0.1', 12140) ) as tunnel: conn = MySQLdb.connect( user='root', password='password123', host='127.0.0.1', database='Db_Name', port=12140) curss = conn.cursor() startdate_oracle_temp = startdate #Формируем цикл по дате. Один оборот цикла обрабатывает один день while startdate_oracle_temp <= enddate: ora_query = all_oracle.format(startdate_oracle_temp.strftime('%d.%m.%Y')) oraclesCursor.execute(ora_query) for raw in oraclesCursor: #В качестве параметров %s, %s, %s, %s, %s переменной insert_query передаем raw cur.execute(insert_query, raw) db.commit() startdate_oracle_temp = startdate_oracle_temp + datetime.timedelta(days=1) cur.close() db.close()
В моем случае в таблице содержалось более 10 млн записей за каждый день.
Можно в запросе к Oracle указать сразу весь диапазон дат, но в случае сбоя этот запрос надо выполнять заново. Если нужна хотя бы часть данных, с которыми необходимо работать, используйте цикл. Преимущества цикла в том, что если Вы выполняете запрос данных за, например, 10-30 дней или более, уже после первого оборота цикла данные за один день будут обработаны и с ними можно работать.
Если Вы зададите весь диапазон дат в сам запрос, придется ждать обработки всех заданных дат и только потом начинать с ними работать.