Выгрузка больших объемов данных из Oracle БД и заливка в MySQL (Python)

Недавно понадобилось перелить большие объемы данных из БД 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 дней или более, уже после первого оборота цикла данные за один день будут обработаны и с ними можно работать.

Если Вы зададите весь диапазон дат в сам запрос, придется ждать обработки всех заданных дат и только потом начинать с ними работать.

Заполнение квадратной матрицы по спирали (Python)

Как заполнить матрицу по спирали

В этой записи я продемонстрирую заполнение квадратной матрицы по спирали на языке Python. Использован Python версии 3.6

#n - размерность матрицы n x n
#mat - результирующая матрица
#st - текущее значение-счетчик для записи в матрицу
#m - коеффициент, используемый для заполнения верхней
#матрицы последующих витков, т.к. одномерные матрицы
#следующих витков имеют меньше значений
n = int(input())
mat = [[0]*n for i in range(n)]
st, m = 1, 0

# Заранее присваиваю значение центральному элементу
# матрицы
mat[n//2][n//2]=n*n

for v in range(n//2):
    #Заполнение верхней горизонтальной матрицы
    for i in range(n-m):
        mat[v][i+v] = st
        st+=1
        i+=1
    #Заполнение правой вертикальной матрицы    
    for i in range(v+1, n-v):
        mat[i][-v-1] = st
        st+=1
        i+=1
    #Заполнение нижней горизонтальной матрицы
    for i in range(v+1, n-v):
        mat[-v-1][-i-1] =st
        st+=1
        i+=1
    #Заполнение левой вертикальной матрицы
    for i in range(v+1, n-(v+1)):
        mat[-i-1][v]=st
        st+=1
        i+=1
    v+=1
    m+=2

#Вывод результата на экран
for i in mat:
    print(*i)

Коротко объясню, для чего нужна переменная m. Обратите внимание на результирующую матрицу при n = 5:

matrix

Начиная со значения 17 мы заполняем новый виток до значения 19. То есть, имеем всего 3 значения: 17, 18, 19.
Для этого и используется коэффициент m, чтобы заново не штудировать все 5 значений.