使用 pyodbc 和 pandas 将 CSV 加载到 .mdb

Posted

技术标签:

【中文标题】使用 pyodbc 和 pandas 将 CSV 加载到 .mdb【英文标题】:Load CSV to .mdb using pyodbc and pandas 【发布时间】:2017-01-02 16:44:55 【问题描述】:

背景故事: 我从事财务工作(不是开发人员,因此非常感谢您的帮助),我的部门目前严重依赖 excel 和 vba 来尽可能多地自动化我们的任务。该公司刚刚验证了一个 python 发行版,我们现在可以使用它,所以我只是想尝试一下。

挑战: 我的第一个挑战是将 CSV 文件加载到 MSAcess 数据库中(因为并不是我们所有人都足够精通技术,可以纯粹使用开发工具和 DB 来工作,因此需要让每个人都轻松完成工作)。

我可以在互联网上找到一些不同的 ppl 代码,我可以将它们放在一起,它可以工作,但结果它变成了一个科学怪人。

它在做什么以及为什么:

    将 CSV 加载到变量中 删除第一行(因为源文件不是真正的 CSV,文件开头有垃圾行) 导出到临时驱动器中的 CSV(因为无法弄清楚如何从变量加载到 panda) 使用 panda 将 CSV 加载到 SQLite(因为 panda 能够推断出每一列的数据类型) 将“create table”语句导出到变量 使用 pyodbc 在 .mdb 文件中创建表 将数据逐行加载到 .mdb 表中(非常慢)

TL;DR: 当前的代码是不同代码的拼凑而成,又丑又慢,你会改变什么来提高效率/优化它?

目标是有一个将 CSV 加载到 .mdb 的代码,可能使用正确的数据类型来创建表。

import csv
import pyodbc
import pandas
import pandas.io.sql
import sqlite3
import tempfile
import time
import string


def load_csv_to_access(access_path, table_name, csv_path, skip_rows):


# open CSV file, load to a variable, output to a temp file excluding first non csv rows
#
filename = csv_path
csv_file = open(filename)
txt = ""
for index, line in enumerate(csv_file, start=0):  #Skip first rows
    if index > skip_rows:
        txt += line
csv_file.close()
temp_filename = time.strftime("%y%m%d%H%M%S") + '.csv'
temp_filepath = tempfile.gettempdir() + '\\' + temp_filename
file = open(temp_filepath, 'w+')
file.write(txt)  # create temp csv
file.close()
print "1: temp file created: " + temp_filepath

# Use panda and SQLite to infer data type of CSV fields
#
df = pandas.read_csv(temp_filepath, delimiter=';', index_col=0, engine='python')
df.columns = df.columns.str.replace(' ', '_')
# connect to in-memory database for testing; replace `:memory:` w/ file path
con = sqlite3.connect('db.sqlite')
df.to_sql(table_name, con, if_exists='replace')
sqlite_query_string = "SELECT sql FROM sqlite_master where name = '" + table_name + "'"
create_table_tuple = con.execute(sqlite_query_string).fetchone()
con.close()
create_table_string = create_table_tuple[0]
print "2: Data type inferred"

#Connect to AccessDB and load temp CSV
#
access_string = "DRIVER=Microsoft Access Driver (*.mdb, *.accdb);DBQ=" + access_path + "; Provider=MSDASQL;"
print access_string
con = pyodbc.connect(access_string)
cur = con.cursor()
cur.execute(create_table_string)
con.commit()
print "3: MS Access table created: " + table_name

print "4: Loading data rows:"
with open(temp_filepath, 'r') as f:
    reader = csv.reader(f, delimiter=';')
    columns = next(reader)
    query = "insert into " + table_name + "(0) values (1)"
    query = query.format(','.join(columns).replace(' ', '_'), ','.join(
        '?' * len(columns)))  #Create insert query (replace empty space by underscore to avoid db issues)
    for index, data in enumerate(reader, start=0):
        cur.execute(query, data)  #Insert row by row
        print index # For debugging
    cur.commit()
con.close()

谢谢,你们比我好得多,如果有任何建议,将不胜感激。

【问题讨论】:

在codereview.stackexchange.com询问这个 Access 也可以推断列类型。您使用 pandas 是因为它做得更好,还是仅仅因为这是您在搜索时发现的? @GordThompson 因为这是我在搜索时可以找到的,不需要从 Access 执行它,甚至不需要打开它。我的计划是拥有一个可以解析不同的 CSV 文件并将其加载到 .mdb 的函数,而不必从 Access 中执行,所以我可以有一个在夜间自动运行的脚本,然后,也许可以通过阅读来改进一个包含 csv 文件列表以及相应的 .mdb 和表名的表。上面的代码有点工作,但速度很慢,特别是最后一部分,它为每一行运行插入查询。 【参考方案1】:

MS Access 可以直接查询 CSV 文件并运行Make-Table Query 以生成结果表。但是,需要进行一些清理以删除 垃圾 行。下面打开两个文件,一个用于读取,另一个用于写入。假设垃圾在 csv 的第一列中,if 逻辑会在第二列中写入任何包含一些数据的行(根据需要进行调整):

import os
import csv
import pyodbc

# TEXT FILE CLEAN
with open('C:\Path\To\Raw.csv', 'r') as reader, open('C:\Path\To\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)

# DATABASE CONNECTION
access_path = "C:\Path\To\Access\\DB.mdb"
con = pyodbc.connect("DRIVER=Microsoft Access Driver (*.mdb, *.accdb);DBQ=;" \
                     .format(access_path))

# RUN QUERY
strSQL = "SELECT * INTO [TableName] FROM [text;HDR=Yes;FMT=Delimited(,);" + \
         "Database=C:\Path\To\Folder].Clean.csv;"    
cur = con.cursor()
cur.execute(strSQL)
con.commit()

con.close()                            # CLOSE CONNECTION
os.remove('C\Path\To\Clean.csv')       # DELETE CLEAN TEMP 

原始 CSV

清理 CSV

MS 访问表

通知访问可以推断列类型,例如第一列中的日期。

【讨论】:

太棒了!谢谢! @Parfait 请问如何将 csv 作为文本加载到数据库中?我有一些数字想保留为文本,但我不知道为什么,谷歌并没有太大帮助。 我的 CSV 用管道分隔,但是当我这样做并指定管道时,它只是将我的所有数据插入到一列中,未分隔。有关如何解决此问题的任何建议? @Mofongo,对于非逗号分隔的文件,如制表符或管道分隔,请在与数据文件位于同一文件夹的 schema.ini 文件中指定分隔符。 this solution.

以上是关于使用 pyodbc 和 pandas 将 CSV 加载到 .mdb的主要内容,如果未能解决你的问题,请参考以下文章

使用 pyODBC 的 fast_executemany 加速 pandas.DataFrame.to_sql

PYODBC 到 Pandas - DataFrame 不起作用 - 传递值的形状是(x,y),索引暗示(w,z)

PYODBC到Pandas - DataFrame不工作 - 传递值的形状是(x,y),索引暗示(w,z)

使用 pyodbc 批量插入 SQL Server 表:找不到文件

如何将 pandas DataFrame 导出到 Microsoft Access?

并行化 pandas pyodbc SQL 数据库调用