Уведомления

Группа в Telegram: @pythonsu

#1 Дек. 6, 2018 12:29:34

sevlovan
От: СПб
Зарегистрирован: 2018-12-05
Сообщения: 14
Репутация: +  0  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Господа,есть работающий код:

 #работа с файлом excel
import pandas as pd
from pandas import DataFrame
read_table = pd.read_excel(r'D:\ОБУЧЕНИЕ\py\gtt.xlsx')
df = DataFrame(read_table)
 
#работа с БД access
import pyodbc
connStr = pyodbc.connect(
    r'Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=D:\ОБУЧЕНИЕ\py\svod.mdb;')
 
#вставляем данные
cursor = connStr.cursor()
for index,\
    row in df.iterrows():
    cursor.execute("INSERT INTO [D:\ОБУЧЕНИЕ\py\svod.mdb].set ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11]) values (?,?,?,?,?,?,?,?,?,?,?)"
                   , row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9],row[10],row[11])
    connStr.commit()
cursor.close()
connStr.close()

помогите сделать так, чтобы в инструкции insert было не такое унылое перечисление как сейчас, а “что-то+1” до последнего столбца в источнике и целевой таблицах.. , а то ведь столбцов может быть много и замучаешься их перечислять да и не элегантно это как-то..

Офлайн

#2 Дек. 6, 2018 14:17:57

Rafik
Зарегистрирован: 2018-09-04
Сообщения: 231
Репутация: +  27  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Вместо

 cursor.execute("INSERT INTO [D:\ОБУЧЕНИЕ\py\svod.mdb].set ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11]) values (?,?,?,?,?,?,?,?,?,?,?)"
                   , row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9],row[10],row[11])
можно использовать распаковку row
[code python]cursor.execute("INSERT INTO [D:\ОБУЧЕНИЕ\py\svod.mdb].set ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11]) values (?,?,?,?,?,?,?,?,?,?,?)"
, *row)[/code]
Перечисление , … можно родить, используя длину row. Через длину row можно родить перечисление ?,?…. Всё это безобразие можно вставить в строку через format, например так:
 stolbci = ''
for ii in range(1,len(row) + 1):
   stolbci += '[{}],'.format(ii)
stolbci = stolbci[:-1]
# print(stolbci)
voprosi = ('?,'*len(row))[:-1]
# print(voprosi)
db = 'D:\ОБУЧЕНИЕ\py\svod.mdb'
zapros = "INSERT INTO [{}].set ({}) values ({})".format(db, stolbci, voprosi)
cursor.execute(zapros, *row)
Такой фокус удаётся только, если порядок столбцов, их кол-во в БД и row совпадают.
Если первый элемент, индекс 0 из row не используется, то предварительно надо взять срез
 row = row[1:]

Отредактировано Rafik (Дек. 6, 2018 14:37:07)

Офлайн

#3 Дек. 6, 2018 16:02:31

sevlovan
От: СПб
Зарегистрирован: 2018-12-05
Сообщения: 14
Репутация: +  0  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Спасибо!

 cursor = connStr.cursor()
for index,\
    row in df.iterrows():
    stolbci = ''
for ii in range(1, len(row) + 1):
    stolbci += '[{}],'.format(ii)
stolbci = stolbci[:-1]
print(stolbci)
voprosi = ('?,' * len(row))[:-1]
print(voprosi)
db = 'D:\ОБУЧЕНИЕ\py\svod.mdb'
zapros = "INSERT INTO [{}].set ({}) values ({})".format(db, stolbci, voprosi)
cursor.execute(zapros, *row)

по данному коду отрабатывает без ошибок, но данные не вставляет..

если убираю * возле row
 cursor.execute(zapros, row)
кидает ошибку
 pyodbc.ProgrammingError: ('The SQL contains 41 parameter markers, but 1 parameters were supplied', 'HY000')

Отредактировано sevlovan (Дек. 6, 2018 16:14:02)

Офлайн

#4 Дек. 6, 2018 19:26:45

Rafik
Зарегистрирован: 2018-09-04
Сообщения: 231
Репутация: +  27  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Звездочка перед row значит, что row передается в функцию в распакованном виде, т.е как бы много параметров, столько, сколько элементов в row. Например:

 row = (1,2,4,'a')
func(*row)
# эквивалентна 
func(1,2,4,'a')
# То же самое, что и
func(row[0], row[1], row[2], row[3])
Если убрать звездочку перед row, то в функцию добавления данных в БД будет передан только один параметр row в виде кортежа/списка (что выдает pandas не знаю точно). Об этом говорит сообщение об ошибке
sevlovan
pyodbc.ProgrammingError: ('The SQL contains 41 parameter markers, but 1 parameters were supplied', ‘HY000’)
требуется 41 параметр, а передаётся только один.
Не вижу строку подтверждения изменений с помощью commit(). Без него ничего не зафиксируется в БД. При закрытии соединения будет “усё потеряно, шеф”(C). Без коммита изменения существуют только для текущего соединения и пока оно не закрыто.
На sqlite коммит делаю только один раз, по завершению цикла добавления данных. Это делает процедуру добавления несколько шустрее, чем с коммитом при каждой итерации. Думаю, что к access это тоже приемлемо.

Отредактировано Rafik (Дек. 6, 2018 19:30:25)

Офлайн

#5 Дек. 6, 2018 20:52:24

sevlovan
От: СПб
Зарегистрирован: 2018-12-05
Сообщения: 14
Репутация: +  0  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Спасибо, уважаемый!
Все доходчиво и продуктивно. Помогли!

Отредактировано sevlovan (Дек. 6, 2018 20:52:51)

Офлайн

#6 Дек. 10, 2018 18:41:30

sevlovan
От: СПб
Зарегистрирован: 2018-12-05
Сообщения: 14
Репутация: +  0  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Rafik

Добрый день!

в продолжение темы: в файле excel у меня данные разного типа: и дата и текст и числа…
в ms access (2010) в таблице все поля сделал memo (ну чтобы наверняка все вошло), но не входит..вот что пишет:

     cursor.execute(zapros, *row)
pyodbc.DataError: ('22008', '[22008] [Microsoft][Драйвер ODBC Microsoft Access]Переполнение поля даты и времени  (36) (SQLParamData)')
при этом, если все данные в excel делаю текстовые, то все записывает без проблем..
на мой выпуклый взгляд это связано с тем, что в поле даты (где преимущественно даты записаны в формате dd-mm-yyyy) встречаются строки со значениями типа: “от 12.01.2017”, потому что пробовал вставлять руками и ячейки с нормальным форматом вставляются, а содержащие строку нет..

как быть, подскажите, пожалуйста?

Офлайн

#7 Дек. 11, 2018 06:41:56

Rafik
Зарегистрирован: 2018-09-04
Сообщения: 231
Репутация: +  27  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Есть два варианта: либо все данные в excell причесать привести к общему виду, либо в питоне проверять тип входных данных, полученных из excell и, если тип не соответствует, преобразовать их к тому виду, который принимает access.
Для приведения к общему виду в excell можно провести замену неправильных разделителей в столбце с датой-временем на правильные и назначение правильного формата ячейки для этого столбца. Этот процесс возможно получится записать в макрос. Если таблица постоянно пополняется, то может быть получится сделать небольшой макрос для excell, который будет проверять корректность ввода и исправлять ошибки. На либреофис такой фокус возможен. Вешается макрос на “Содержимое изменено” в событиях листа
Дату-время желательно хранить в БД в формате дата-время. В дальнейшем будет проще писать запросы с использованием фильтров и вычислений с датой-временем.

Офлайн

#8 Дек. 11, 2018 15:45:40

sevlovan
От: СПб
Зарегистрирован: 2018-12-05
Сообщения: 14
Репутация: +  0  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Rafik

Дату-время желательно хранить в БД в формате дата-время. В дальнейшем будет проще писать запросы с использованием фильтров и вычислений с датой-временем.
Я планировал сначала записать все данные в мемо а потом поменять тип данных на приемлемый для построения запросов и отчетов.
Понятно, что часть данных “изыдет”, как несоответствующая типу данных, но это уже будут проблемы поставщиков данных..

Сейчас очевидно, что данная схема не срабатывает.
Предложенный Вами вариант по причесыванию данных в excel тоже не подходит, т.к. таблиц, которые планируется сгружать в access порядка 40 (получаемых ежемесячно) и вся затея как раз для того, чтобы этого избежать - т.е. получил-питоном обработал в access загрузил.

соответственно весьма приемлемым мне представляется другой указанный Вами способ:
либо в питоне проверять тип входных данных, полученных из excell и, если тип не соответствует, преобразовать их к тому виду, который принимает access.
но как..?

Отредактировано sevlovan (Дек. 11, 2018 15:46:30)

Офлайн

#9 Дек. 11, 2018 19:16:17

Rafik
Зарегистрирован: 2018-09-04
Сообщения: 231
Репутация: +  27  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Я не работал с pandas и уровень общения с excell файлами ограничивается старым форматом xls, который читаю с помощью xlrd. Для новых форматов 2010 (xlsx) требуется библитека openpyxl. Это не важно.
Перейдём к нашим баранам, т.е. упрямым и некорректным данным.
Перво-наперво надо будет посмотреть как выглядят корректные данные, которые принимает access. Именно как выглядит содержимое корректной ячейки после прочтения pandas-ом.
Собрать максимальное количество всевозможных уникальных образцов данных, которые не соответствуют правильному шаблону.
Первый образец уже есть

sevlovan
“от 12.01.2017”
Это можно разбить с помощью метода split() по пробелу, взять последний элемент и привести, например, к виду 12-01-2017 с помощью метода replace()
Как-то так
 cell_value = 'от 12.01.2017'
print(cell_value.split(' ')[-1].replace('.', '-'))
Если всяких вариантов написания даты будет много и будет сильно разношёрстно, то может быть стоит попробовать регулярные выражения. В них я не силён, но надеюсь ребята смогут подсказать.

Офлайн

#10 Дек. 13, 2018 16:33:43

sevlovan
От: СПб
Зарегистрирован: 2018-12-05
Сообщения: 14
Репутация: +  0  -
Профиль   Отправить e-mail  

Перенос данных из excel в access, используя pandas

Rafik, добрый вечер!

мне тут подсказали, что эффективней мою задачу можно решить методом: xls –> csv –> mdb

вот мое новое “творчество”, наглым образом скопипащенное со stackoverflow и адаптированное под мои нужды:

 import xlrd
import csv
import os
import pyodbc
with xlrd.open_workbook('D:\ОБУЧЕНИЕ\py\gtt.xls') as wb:
    sh = wb.sheet_by_index(0)
    with open('gtt.csv', 'w', newline="") as f:
        c = csv.writer(f)
        for r in range(sh.nrows):
            c.writerow(sh.row_values(r))
with open('D:\ОБУЧЕНИЕ\py\gtt.csv', 'r') as reader, open('D:\ОБУЧЕНИЕ\py\gtt_clean.csv', 'w') as writer:
    read_csv = csv.reader(reader); write_csv = csv.writer(writer, lineterminator='\n')
    for line in read_csv:
        if len(line[1]) > 0:
            write_csv.writerow(line)
access_path = "D:\ОБУЧЕНИЕ\py\\svod.mdb"
con = pyodbc.connect("DRIVER={{Microsoft Access Driver (*.mdb, *.accdb)}};DBQ={};" \
                     .format(access_path))
strSQL = "SELECT * INTO [set_s] FROM [text;HDR=Yes;FMT=Delimited(,);" + \
         "Database=D:\ОБУЧЕНИЕ\py].gtt_clean.csv;"
cur = con.cursor()
cur.execute(strSQL)
con.commit()
тут, кстати говоря, и проблема даты не то чтобы исчерпала себя, но для моего уровня стала вполне посильна для автоматизированного устранения..

и все бы ничего, но этот код обрабатывает 1 файл, а у меня их порядка 40 … т.е. задумка такая, я кладу их в некую папку и они оттуда перекочевывают в одну таблицу в mdb..
пробовал использовать glob:
 from glob import glob
g = glob('*.xls')
for fname in glob
но куда и как его приартачить “мой уровень” мне не позволил.. мне конечно glob не принципиален, может что другое сгодится..
не откажите и на сей раз..

p.s. спешу уверить, что прежние Ваши труды не пошли прахом, и как минимум расширили область моего незнания (если по Сократу..)

Отредактировано sevlovan (Дек. 13, 2018 16:39:05)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version