不能用append()循环写万行数据,因每次调用均重建单元格对象、刷新样式缓存,致耗时超2分钟、内存飙升至500mb+;应一次性转二维列表或dataframe后单次append,或用cell()直接赋值。

能导出,但别直接用 openpyxl 一行行写数据——性能差、易出错、内存爆掉是常态。
为什么不能用 append() 循环写万行数据?
常见错误是查完 MySQL 结果集后,对每行调一次 ws.append(row)。这看似简单,实际会触发 openpyxl 每次都重建单元格对象、反复刷新样式缓存,1 万行耗时可能超 2 分钟,内存占用飙升到 500MB+。
真正该做的是:一次性把数据转成二维列表或 pandas.DataFrame,再用 ws.append() 批量写入(只调一次),或者更优——用 ws.cell() 直接定位赋值。
-
cursor.fetchall()返回的是 tuple 元组列表,需转成 list 或保持原样传给append()(它支持 tuple) - 如果字段含 datetime、Decimal、None,
openpyxl默认不识别,得提前转成str或float;否则报ValueError: Cannot convert <class> to Excel</class> - 别在循环里调
wb.save(),必须全部写完再保存一次
如何安全处理 MySQL 的 NULL、datetime 和 Decimal 字段?
MySQL 驱动(如 pymysql 或 mysql-connector-python)返回的 None、datetime.datetime、decimal.Decimal 不能直接塞进 Excel 单元格。
最稳妥的做法是在写入前统一清洗:
- 用
row = [v if v is not None else "" for v in row]处理 NULL - 对
datetime类型字段,用v.strftime("%Y-%m-%d %H:%M:%S")转字符串(或保留为datetime对象,openpyxl 支持,但 Excel 单元格格式需手动设为日期) - 对
Decimal,用float(v)或str(v)——选 float 更利于后续 Excel 公式计算
示例清洗逻辑:
def clean_row(row):
return [
"" if v is None else
v.strftime("%Y-%m-%d %H:%M:%S") if hasattr(v, "strftime") else
float(v) if hasattr(v, "as_tuple") else # decimal.Decimal
v
for v in row
]
怎么让表头自动加粗、冻结首行、列宽自适应?
openpyxl 不会自动美化,但几行代码就能搞定基础排版:
- 表头加粗:
for cell in ws[1]: cell.font = Font(bold=True) - 冻结首行:
ws.freeze_panes = "A2" - 列宽自适应(注意:不能真“自适应”,只能按字符数估算):
for column_cells in ws.columns: length = max(len(str(cell.value)) for cell in column_cells); ws.column_dimensions[column_cells[0].column_letter].width = min(length + 2, 50)
别用 ws.auto_filter 自动加筛选器——它只对连续非空区域生效,且必须在写完所有数据后设置:ws.auto_filter.ref = ws.dimensions(但要确保第一行是纯表头、无合并单元格)
导出大表(>10 万行)的替代方案
openpyxl 在百万行级场景下会卡死或 OOM。这时候别硬扛:
- 改用
xlsxwriter:它流式写入、内存友好,但不支持读取已有文件 - 分页导出:用
LIMIT+OFFSET分批查,每批 5 万行,生成多个 sheet 或多个文件 - 跳过 Excel,导出 CSV:用
csv.writer写文件,再用 Excel 打开(兼容性好、速度快、无内存压力)
真正麻烦的不是“怎么导出”,而是“导出后用户要不要在 Excel 里做公式、透视、筛选”——如果要,就得保格式、保类型、保冻结;如果只是看数,CSV 真的够用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











