把Excel里的数据搬到数据库,是后台开发和数据分析岗都常碰到的活。小文件用循环插也能凑合,但数据上了规模,方法选错就会卡死或者锁表。下面从读取、拼接、执行三个层面把这件事讲透。

一、Excel文件的读取方式
最常用的Python库是openpyxl和pandas。openpyxl适合精细控制单元格,比如只取某个sheet、某几列;pandas则一行代码把整表变成DataFrame,后续处理更顺手。如果Excel是老版的xls,要用xlrd,但新版本xlrd已不支持xls以外的格式,需注意环境兼容。
读取时建议显式指定数据类型,不要完全依赖自动推断。例如手机号在Excel里可能是数字,直接读会变成整数从而丢掉前导零。用openpyxl可以把单元格value先转成字符串,避免入库后位数不对。
from openpyxl import load_workbook
wb = load_workbook('data.xlsx', read_only=True)
ws = wb['Sheet1']
rows = []
for row in ws.iter_rows(min_row=2, values_only=True):
# 强制转字符串,保住前导零
phone = str(row[2])
rows.append((row[0], row[1], phone))
二、单条插入与批量插入的差距
最直观的错误写法是在循环里反复调用execute执行insert。每次都要走网络、做语法解析、写日志,几万次下来耗时惊人。我们做过简单对比:本地MySQL插入五万行,逐条插用了将近两分钟,而拼成多值语句一次executemany只用不到三秒。
批量插入的核心是把多行值合并到一条SQL里,或者交给驱动做批量协议。下面是用executemany的示例,占位符随数据库不同可能是%s或?,MySQL用%s。
import pymysql conn = pymysql.connect(host='127.0.0.1', user='root', password='pass', db='test') cur = conn.cursor() sql = "INSERT INTO user (name, age, phone) VALUES (%s, %s, %s)" cur.executemany(sql, rows) conn.commit() cur.close() conn.close()
三、利用数据库原生导入工具
如果数据量达到几十万甚至上百万,最猛的方案是数据库自带的文件导入命令。MySQL的load data infile能直接读csv或文本,绕过SQL层,速度最快。做法是先把Excel另存为csv,注意分隔符和编码,然后用一条命令灌进去。
使用时要留意服务端权限,local参数允许客户端传文件,否则文件需在数据库服务器本地。同时字段顺序、行终止符要跟表结构对上,否则会报截断错误。下面给出一条典型语句。
LOAD DATA LOCAL INFILE '/tmp/data.csv' INTO TABLE user FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY 'n' (name, age, phone);
四、常见坑与处理办法
Excel里的日期本质是序列号,直接写库会变成数字。读出来时用datetime模块转换,或者让pandas在读取时指定parse_dates。另外单元格内的换行和逗号会破坏csv结构,导出时要用引号包裹,或在代码里做转义替换。
字符编码也常出问题,Excel默认存成gbk,数据库若是utf8mb4,不转码就会乱码。用pandas的to_csv时显式写encoding='utf-8',并在load语句里加character set utf8mb4。字段超长会触发截断,提前比对长度能少踩坑。
| 方案 | 适用规模 | 速度 | 复杂度 |
|---|---|---|---|
| 循环单插 | 千行内 | 慢 | 低 |
| executemany | 十万行内 | 较快 | 中 |
| load data | 百万行级 | 极快 | 中高 |
五、小结思路
选方案先看数据量:小批量图省事用executemany,大批量直接走原生导入。无论哪种,先把Excel读准、类型转对、编码统一,再去谈性能。把读取和写入拆成两步,也方便中途校验和断点重跑。
实际项目里还可以加个临时表,先全量落地再清洗进正式表,这样出错能回滚重来,不至于污染业务数据。理清这条链路,Excel入库就不再是一个头疼任务。