如何用Python将数据批量的插入到数据库

导读我是一名挣扎在编程链底端的pythoner,工作中既要和数据打交道,也要保持和erp系统,web网站友好的"沟通"···,我会时不时的分享下工作中遇到那点事,包括个人觉得值得记录的编程小技巧,还有就是遇到的问题以及解决方案,还有源码的阅读等等,可能也有编程中的生活感悟,不说了,我要去重构我的程序了

本文基于python, 使用pandas, pymysql等三方库实现了向数据库中高效批量插入数据,一方面提供被网上很多瞎转载的答案给坑蒙了的人(因为我也是),一方面自己也做个笔记,以后方便查阅.

需求原因

最近在处理一个需求,有关批量往数据库插入数据的,描述如下

  • 原来的程序是基于sql的存储过程进行数据的更新修改操作,由于数据量较大,导致对数据库压力太大,于是需要将程序重构为用python读取文件的方式将数据做计算处理,减少这部分的压力,最后仅仅将计算的结果调用aws的lambda服务重新更新到数据库中就可以了,减少了极大的压力,也降低了成本。涉及数据库主要是插入及更新操作

版本库信息

  • 三方库 >>> pandas 1.0.5, pymysql 0.9.3
  • python版本 >>> 3.7
  • 标准库 >> os

逻辑梳理

实际上,最后一步,要写入数据库的文件数据是存储在内存中的。因为读取文件后进行的计算都是在内存中进行的,那么计算的结果也没必要再写到本地,再去读取,再写入数据库,这是会影响程序的效率的。逻辑如下

  • 读取文件
  • 文件的拼接及计算,生成新的df
  • 初始化数据库的连接
  • 将df所需数据转换为元组数据(取决于数据库的三方库的接口是如何支持批量操作的)
  • 将数据写入数据库
  • 检查数据库内容即可

分步实现及分析

读取文件

给文件路径,然后去读文件就行了,强调一下需要注意的点

  • 绝对路径: 这种最简单,直接给路径字符串就行了,但是一旦文件夹目录结构变化,就需要频繁的改
  • 相对路径: 我一般喜欢先在脚本中定位当前脚本的位置,然后通过相对路径去找,这样只要你整个包内部的目录结构不变化,都不用改,就算部署上线也是直接根据包的位置来,很方便
  • pandas默认会将所有数字读取为float类型,所以对于那种看起来是数字,但实际上是需要当作字符串使用的字段进行类型的转换
import pandas as pd  
import numpy as np 
 
# 当前脚本的位置 
current_folder_path = os.path.dirname(__file__) 
 
# 你的文件的位置 
your_file_path1 = os.path.join(current_folder_path, "文件的名字1") 
your_file_path2 = os.path.join(current_folder_path, "文件的名字2") 
 
# 我这里是以读取csv文件为例, delimiter为我们内部约定的列之间的分割符 
df1 = pd.read_csv(your_file_path1, dtype={"column1": str, "column2": str}, delimiter="/t") 
df2 = pd.read_csv(your_file_path2, dtype={"column1": str, "column2": str}, delimiter="/t") 

文件的拼接及计算

文件的拼接主要就是merge和concat两个语法的使用,强调一下小知识点

  • merge语法主要是对应于sql语言的内连接,外连接,左连接和右连接等
  • concat主要是用来将相同结构的df单纯的拼接起来(也就是列表的总行数增加)
# 这里以左连接举例, 假设只有两个文件拼接 
ret_df = pd.merge(df1, df2, left_on=["column_name"], right_on=["column_name"], how="left") 

初始化连接

导入三方库pymysql,初始化连接

# pymysql的接口获取链接 
def mysql_conn(host, user, password, db, port=3306, charset="utf8"): 
  # 传参版本 
  conn = pymysql.connect(host=host, user=user, password=password, database=db, port=port, charset=charset) 
  return conn 

对应接口转换数据

1.数据插入要考虑写入一个事务,因为失败的话,要保证对数据库没有影响
2.构造符合对应接口的数据格式,通过查询,pymysql有两种可以执行语句的接口

  • execute(单条插入语句)
  • 执行单条语句的接口

1.类似这种: Insert into table_name (column) values (value);
2.executemany(批量插入语句)

  • 执行多条语句的接口
  • 类似这种: Insert into table_name (column1, column2, column3) values (value1, value2, value3);

具体实现如下

# 先创建cursor负责操作conn接口 
conn = mysql_conn("your db host", "your username", "your password", "db name") 
cursor = conn.cursor() 
# 开启事务 
conn.begin() 
 
#############      构造批量数据的过程            ############# 
 
# 先构造需要的或是和数据库相匹配的列 
columns = list(df.columns) 
# 可以删除不要的列或者数据库没有的列名 
columns.remove("列名") 
# 重新构造df,用上面的columns,到这里你要保证你所有列都要准备往数据库写入了 
new_df = df[columns].copy() 
 
# 构造符合sql语句的列,因为sql语句是带有逗号分隔的,(这个对应上面的sql语句的(column1, column2, column3)) 
columns = ','.join(list(new_df.columns)) 
 
# 构造每个列对应的数据,对应于上面的((value1, value2, value3)) 
data_list = [tuple(i) for i in gdsord_df.values] # 每个元组都是一条数据,根据df行数生成多少元组数据 
 
# 计算一行有多少value值需要用字符串占位 
s_count = len(data_list[0]) * "%s," 
 
# 构造sql语句 
insert_sql = "insert into " + "数据库表名" + " (" + columns + ") values (" + s_count[:-1] + ")" 

将数据写入数据库

这个简单,直接上代码

cursor.executemany(insert_sql, data_list) 
conn.commit() 
cursor.close() 
conn.close() 

检查数据库是否插入成功

如果没问题的话,就可以同时进行多个文件读写,计算,最后启用多线程同时向数据库中写入数据了,非常高效!

完整代码

import pandas as pd  
import numpy as np 

# pymysql接口 
def mysql_conn(host, user, password, db, port=3306, charset="utf8"): 
  conn = pymysql.connect(host=host, user=user, password=password, database=db, port=port, charset=charset) 
  return conn 
 
 
# 当前脚本的位置 
current_folder_path = os.path.dirname(__file__) 
 
# 你的文件的位置 
your_file_path1 = os.path.join(current_folder_path, "文件的名字1") 
your_file_path2 = os.path.join(current_folder_path, "文件的名字2") 
 
# 我这里是以读取csv文件为例, delimiter为我们内部约定的列之间的分割符 
df1 = pd.read_csv(your_file_path1, dtype={"column1": str, "column2": str}, delimiter="/t") 
df2 = pd.read_csv(your_file_path2, dtype={"column1": str, "column2": str}, delimiter="/t") 
# 合并 
ret_df = pd.merge(df1, df2, left_on=["column_name"], right_on=["column_name"], how="left") 
 
# 先创建cursor负责操作conn接口 
conn = mysql_conn("your db host", "your username", "your password", "db name") 
cursor = conn.cursor() 
# 开启事务 
conn.begin() 
 
# 先构造需要的或是和数据库相匹配的列 
columns = list(df.columns) 
# 可以删除不要的列或者数据库没有的列名 
columns.remove("列名") 
# 重新构造df,用上面的columns,到这里你要保证你所有列都要准备往数据库写入了 
new_df = df[columns].copy() 
 
# 构造符合sql语句的列,因为sql语句是带有逗号分隔的,(这个对应上面的sql语句的(column1, column2, column3)) 
columns = ','.join(list(new_df.columns)) 
 
# 构造每个列对应的数据,对应于上面的((value1, value2, value3)) 
data_list = [tuple(i) for i in gdsord_df.values] # 每个元组都是一条数据,根据df行数生成多少元组数据 
 
# 计算一行有多少value值需要用字符串占位 
s_count = len(data_list[0]) * "%s," 
 
# 构造sql语句 
insert_sql = "insert into " + "数据库表名" + " (" + columns + ") values (" + s_count[:-1] + ")" 
try: 
  cursor.executemany(insert_sql, data_list) 
  conn.commit() 
  cursor.close() 
  conn.close() 
except Exception as e: 
  # 万一失败了,要进行回滚操作 
  conn.rollback() 
  cursor.close() 
  conn.close() Linux就该这么学
<think>我们被要求使用Python将字典批量插入到MySQL数据库。根据引用内容,我们可以归纳出几种方法,并针对批量插入进行优化。 方法分析: 1. 单条插入:循环遍历字典列表,对每个字典构造插入语句并执行(引用[1]提供了单条插入的示例)。 2. 批量插入:使用`executemany`方法(引用[4])可以一次性插入多条数据,这通常比单条插入效率高。 注意:字典的键对应数据库表的列名,值对应要插入数据。 步骤: 1. 连接数据库。 2. 准备数据:一个字典列表,每个字典代表一行数据,且每个字典的键结构相同。 3. 构造插入语句: - 提取字典的键(列名) - 构造占位符(例如:%s) - 构造SQL语句模板 4. 构造值列表:从每个字典中提取与键顺序一致的值,组成元组,并放入列表中。 5. 使用`executemany`执行批量插入。 示例代码: 假设我们有一个字典列表`data_list`,每个字典的结构为:`{'col1': value1, 'col2': value2, ...}`。 数据库表名为`mytable`。 步骤如下: 1. 连接数据库(这里使用pymysql) 2. 获取第一个字典的键(假设所有字典都有相同的键),用于构造列名字符串。 3. 根据键的数量构造占位符字符串(如:%s, %s, ...)。 4. 构造SQL语句:`INSERT INTO mytable (col1, col2, ...) VALUES (%s, %s, ...)` 5. 提取所有字典的值组成元组列表。 注意:字典的键顺序必须一致,否则需要按相同顺序排列。 代码示例: ```python import pymysql # 假设我们有如下字典列表 data_list = [ {'name': 'Alice', 'age': 25, 'city': 'New York'}, {'name': 'Bob', 'age': 30, 'city': 'Chicago'}, {'name': 'Charlie', 'age': 35, 'city': 'Los Angeles'} ] # 连接数据库 db = pymysql.connect(host='localhost', user='root', password='password', database='test') cursor = db.cursor() # 提取列名(键),这里假设所有字典键相同,取第一个字典的键 if data_list: keys = data_list[0].keys() columns = ', '.join(keys) placeholders = ', '.join(['%s'] * len(keys)) # 构造SQL语句 sql = f"INSERT INTO mytable ({columns}) VALUES ({placeholders})" # 构造值列表:每个字典的值按照键的顺序组成元组 values_list = [] for data in data_list: # 确保值的顺序与列名顺序一致 values = tuple(data[key] for key in keys) values_list.append(values) # 使用executemany批量插入 cursor.executemany(sql, values_list) db.commit() print("批量插入成功") else: print("无数据插入") # 关闭连接 cursor.close() db.close() ``` 注意事项: 1. 数据库连接信息需根据实际情况修改。 2. 异常处理:实际应用中应添加异常处理,以便在出错时回滚事务。 3. 数据验证:确保每个字典中都有相同的键,且值的类型与数据库表定义匹配。 4. SQL注入:使用参数化查询(%s占位符)可以避免SQL注入。 如果需要插入的字典列表非常大,可以考虑分批次插入(例如每次插入1000条)以避免内存问题和数据库连接超时。 引用[4]中使用了`executemany`方法,这是高效批量插入的常用方式。 相关问题: 1. 如何优化大批量数据插入性能? 2. 插入时遇到主键冲突怎么办? 3. 如何插入包含特殊数据类型(如日期、时间、二进制)的字典? 根据引用内容,我们实现了将字典批量插入MySQL的方法。 </think>### Python实现字典批量插入MySQL数据库的方法 根据引用内容并优化批量操作,以下是高效可靠的实现方法: #### 核心步骤 1. **数据准备**:将多个字典存入列表 `data_list = [{dict1}, {dict2}, ...]` 2. **SQL构造**: ```python # 获取所有字典的键(需确保所有字典结构一致) keys = ', '.join(data_list[0].keys()) # 生成占位符 (如:%s, %s, %s) placeholders = ', '.join(['%s'] * len(data_list[0])) # 构建SQL模板 sql = f"INSERT INTO {table_name} ({keys}) VALUES ({placeholders})" ``` 3. **批量插入**: ```python # 提取所有字典的值生成元组列表 values_list = [tuple(item.values()) for item in data_list] # 使用executemany高效插入 cursor.executemany(sql, values_list) db.commit() ``` #### 完整示例代码 ```python import pymysql # 数据库连接 db = pymysql.connect( host='localhost', user='your_username', password='your_password', database='your_db' ) # 准备批量字典数据 data_list = [ {'name': 'Alice', 'age': 25, 'city': 'New York'}, {'name': 'Bob', 'age': 30, 'city': 'Chicago'}, {'name': 'Charlie', 'age': 28, 'city': 'Los Angeles'} ] try: with db.cursor() as cursor: # 动态构建SQL keys = ', '.join(data_list[0].keys()) placeholders = ', '.join(['%s'] * len(data_list[0])) sql = f"INSERT INTO users ({keys}) VALUES ({placeholders})" # 提取值并批量执行 values_list = [tuple(item.values()) for item in data_list] cursor.executemany(sql, values_list) db.commit() print(f"成功插入{cursor.rowcount}条数据") finally: db.close() ``` #### 关键优化点 1. **使用executemany**:比循环单条插入快10-100倍(减少网络往返)[^4] 2. **事务提交**:批量操作后统一commit提升性能 3. **参数化查询**:避免SQL注入风险(使用`%s`占位符) 4. **连接管理**:使用上下文管理器确保资源释放 #### 特殊数据类型处理 若字典包含列表等非标量数据(如引用[2]案例): ```python # 转换示例:将列表转为JSON字符串 data = {'id': 1, 'tags': ['A', 'B']} data['tags'] = json.dumps(data['tags']) # 存入前序列化 # 查询时使用JSON_EXTRACT解析 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值