如何通过 executemany() 语句转换 pandas 数据框以进行插入?

Posted

技术标签:

【中文标题】如何通过 executemany() 语句转换 pandas 数据框以进行插入?【英文标题】:how to transform pandas dataframe for insertion via executemany() statement? 【发布时间】:2015-07-08 10:28:54 【问题描述】:

我有一个相当大的 pandas dataframe - 50 左右的标头和几十万行数据 - 我希望使用 ceODBC 模块将这些数据传输到数据库。以前我使用pyodbc 并在 for 循环中使用一个简单的执行语句,但这花费的时间非常长(每 10 分钟 1000 条记录)...

我现在正在尝试一个新模块并尝试引入executemany(),尽管我不太确定以下参数序列的含义:

    cursor.executemany("""insert into table.name(a, b, c, d, e, f) 
values(?, ?, ?, ?, ?), sequence_of_parameters)

它应该看起来像一个通过每个标题工作的常量列表吗

    ['asdas', '1', '2014-12-01', 'true', 'asdasd', 'asdas', '2', 
'2014-12-02', 'true', 'asfasd', 'asdfs', '3', '2014-12-03', 'false', 'asdasd']
这是三行的示例

或者需要什么格式?

作为另一个相关问题,我该如何将常规的 pandas 数据帧转换为这种格式?

谢谢!

【问题讨论】:

dfs 有什么关系?它们都是相同较大df的子集吗?您可以合并、连接它们并一次性写入数据库 嗨,埃德。这都是一个单一的数据框,我认为它们必须被溢出到所有不同标题的子集中才能在查询中工作......所以'a','b','c'等等表示不同的列标题对于数据框 你试过pymssql吗? (但我没有经验,如果这比pyodbc 更快)但是你可以使用to_sql pandas 方法(驱动程序必须由 sqlalchemy 支持才能与 pandas 一起使用) 【参考方案1】:

你可以试试这个:

cursor.executemany(sql_str, your_dataframe.values.tolist())

希望对你有帮助。

【讨论】:

能否请您详细说明您的答案,添加更多关于您提供的解决方案的描述?【参考方案2】:

我最终设法解决了这个问题。 因此,如果您有一个 Pandas 数据框,您想使用我使用的模块 ceODBC 将其写入数据库,代码是:

(以all_data 作为数据框)将数据框值映射到字符串并将每一行作为一个元组存储在一个元组列表中

for r in all_data.columns.values:
    all_data[r] = all_data[r].map(str)
    all_data[r] = all_data[r].map(str.strip)   
tuples = [tuple(x) for x in all_data.values]

对于元组列表,将所有空值指示符(已在上面的转换中捕获为字符串)更改为可以传递给最终数据库的空类型。这对我来说是个问题,可能不适合你。

string_list = ['NaT', 'nan', 'NaN', 'None']

def remove_wrong_nulls(x):
    for r in range(len(x)):
        for i,e in enumerate(tuples):
            for j,k in enumerate(e):
                if k == x[r]:
                    temp=list(tuples[i])
                    temp[j]=None
                    tuples[i]=tuple(temp)

remove_wrong_nulls(string_list)

创建与数据库的连接

cnxn=ceODBC.connect('DRIVER=SOMEODBCDRIVER;DBCName=XXXXXXXXXXX;UID=XXXXXXX;PWD=XXXXXXX;QUIETMODE=YES;', autocommit=False)
cursor = cnxn.cursor()

定义一个函数,将元组列表转换为new_list,这是对元组列表的进一步索引,分为 1000 个块。这对于我将数据传递到 SQL 查询不能的数据库是必要的超过 1MB。

def chunks(l, n):
    n = max(1, n)
    return [l[i:i + n] for i in range(0, len(l), n)]

new_list = chunks(tuples, 1000)

定义您的查询。

query = """insert into XXXXXXXXXXXX("XXXXXXXXXX", "XXXXXXXXX", "XXXXXXXXXXX") values(?,?,?)"""

遍历包含 1000 个组中的元组列表的 new_list 并执行 executemany。通过提交和关闭连接来遵循这一点,就是这样:)

for i in range(len(new_list)):
    cursor.executemany(query, new_list[i])
cnxn.commit()
cnxn.close()

【讨论】:

map(str) 似乎有点笨拙;有没有更直接的方法?【参考方案3】:

回答这个问题可能有点晚了,但也许它仍然可以帮助某人。 executemany() 没有被许多 ODBC 实现。其中之一是mysql。当他们提到参数序列时,他们的意思是:

parameters=['name':'Jorge', 'age':22, 'sex':'M', 
            'name':'Karen', 'age':25, 'sex':'F', 
            'name':'James', 'age':29, 'sex':'M']

对于查询语句,它看起来像:

SQL = INSERT IGNORE INTO WORKERS (NAME, AGE, SEX) VALUES (%(name)s, %(age)s, %(sex)s)

看起来你已经到了那里。尽管我想指出一些事情,以防万一它有帮助: pandas 有一个 to_sql 函数,如果你向它提供连接器对象,它会插入到数据库中,并且还会对数据进行分块。

为了从 pandas 数据框快速创建一系列参数,我发现以下两种方法很有帮助:

# creates list of dict, list of parameters
# REF: https://groups.google.com/forum/#!topic/pydata/qna3Z3WmVpM
parameters = [df.iloc[line, :].to_dict() for line in range(len(df))]

# Cleaner Way
parameters = df.to_dict(orient='records')

【讨论】:

使用最后一种创建字典列表的方法会导致列名在每个字典中按字母顺序排序。知道如何手动排序吗? 你的意思是字典中的键是按字母顺序排列的?不保证字典键是有序的。如果您希望订购它们,您需要使用OrderedDictdf.to_dict(into=OrderedDict) 嗨,我在这里需要更多解释,我有列表(如您上面定义的“参数”列表)现在我必须将此值存储到 MySQL 表名“用户”,你能给我在 Flask 中将其存储到包含列名称、年龄和性别的表中的方法【参考方案4】:

概括一下以确保查询和数据框的列顺序相同:

columns = ','.join(df.columns)
values=','.join(['::d'.format(i+1) for i in range(len(df.columns))])

sql = 'INSERT INTO table.name(columns:) VALUES (values:)'
cursor.executemany(sql.format(columns=columns, values=values), df.values.tolist())

【讨论】:

以上是关于如何通过 executemany() 语句转换 pandas 数据框以进行插入?的主要内容,如果未能解决你的问题,请参考以下文章

Python SQL executemany 语句不起作用

Executemany混乱

(fast_executemany = True) 错误“[ODBC Driver 17 for SQL Server]强制转换规范 (0) (SQLExecute)'的字符值无效”)

Python executemany 函数

Python MySQL executemany的使用

ExecuteMany 插入错误的值