在异构数据流转场景中,Excel常被用作业务数据的中间载体。但要把它导入MySQL,并不意味着数据库能直接识别.xlsx文件。Excel工作簿包含多个工作表、合并单元格、公式缓存和自定义格式,而MySQL需要行列表格数据。因此实现思路通常分成两类:先把Excel转为CSV或DataFrame等结构化中间格式,再批量写入MySQL;或者借助工具自动完成格式转换与字段映射。下面从原理到实践逐步展开。

一、先厘清Excel与MySQL的数据差异
Excel的单元格没有强制类型约束,数字可以保存为文本,日期可以显示成各种格式,空白列和空单元格也被当成普通内容处理。MySQL则完全不同,每一列在建表时就要确定数据类型,例如整数列不允许出现字母,日期列必须符合日期格式。直接把.xlsx文件解析后插入数据库,最容易出现的问题就是数据截断、类型不匹配以及无法识别的空值。
导入前必须先规划目标表结构。建议在MySQL中创建与Excel列顺序一致的临时表,所有字段优先使用更宽松的类型,例如字符串列使用VARCHAR,金额列使用DECIMAL,日期列使用DATE或DATETIME。字符集统一使用utf8mb4,排序规则使用utf8mb4_unicode_ci,这样能避免中文乱码。如果Excel中某一列混合了数字和文本,可以先按VARCHAR导入,后续再用CAST或CONVERT清洗。
另一个容易被忽略的差异是公式缓存。如果Excel单元格由公式计算而来,却没有重新计算并保存,解析工具读到的可能是一个旧值,甚至读不到值。建议在导出前先复制并粘贴为值,或者另存为CSV格式,这样只会保留计算结果而不会携带底层公式。
二、方案一:CSV配合LOAD DATA INFILE高速导入
对于数据量较大的导入任务,使用MySQL自带的LOAD DATA INFILE命令是性能最好的方案。它的原理是从文本文件中按行读取内容,再根据字段分隔符拆分并写入数据表。Excel并不能直接被该命令读取,因此需要先在Excel中另存为CSV UTF-8格式。CSV文件本质上是逗号分隔的纯文本,正好符合LOAD DATA INFILE的解析方式。
首先确认MySQL允许加载文件。可以用SHOW VARIABLES LIKE 'secure_file_priv';查看允许读取的目录。如果该值不为空,就必须把CSV文件放到对应目录下,例如Windows平台常见路径是C:\ProgramData\MySQL\MySQL Server 8.0\Uploads\。如果值为空,说明没有目录限制,但仍需确保MySQL进程对文件有读取权限。同时检查local_infile是否开启,必要时执行SET GLOBAL local_infile = 1;。
下面是一个完整的SQL示例,假设CSV文件为emp.csv,包含工号、姓名、入职日期和薪资四列。
-- 检查文件目录限制
SHOW VARIABLES LIKE 'secure_file_priv';
-- 允许本地文件导入
SET GLOBAL local_infile = 1;
-- 创建目标表
CREATE TABLE emp_import (
emp_no VARCHAR(20),
emp_name VARCHAR(50),
hire_date DATE,
salary DECIMAL(10,2)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 从CSV文件导入数据
LOAD DATA INFILE 'C:/ProgramData/MySQL/MySQL Server 8.0/Uploads/emp.csv'
INTO TABLE emp_import
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(emp_no, emp_name, hire_date, salary);
执行时需要注意FIELDS TERMINATED BY ','指定逗号为字段分隔符,IGNORE 1 LINES表示跳过文件首行的列标题。如果CSV中的文本字段用双引号包围,可以加上OPTIONALLY ENCLOSED BY '"',否则双引号会被当成普通字符写入。日期列能否正确导入取决于字符串格式是否符合MySQL的日期解析规则,通常YYYY-MM-DD格式最稳妥。
如果文件必须从客户端机器读取,而不是数据库服务器本地读取,可以把命令换成LOAD DATA LOCAL INFILE。需要注意LOCAL导入有时会被安全策略禁用,可以通过SET GLOBAL local_infile = 1;开启。该方案的优势是速度快,批量写入百万行数据也只需几十秒,但不够灵活,不适合需要先处理空值、重复值或字段映射复杂的场景。
三、方案二:使用Python pandas实现自动化导入
如果Excel文件需要反复导入,或者导入前必须做去重、转换日期、填充默认值等清洗工作,使用Python的pandas库会更合适。pandas能够直接读取.xlsx文件,并通过DataFrame对象完成各种数据操作,最后再批量写入MySQL。安装依赖时通常需要pandas、openpyxl、sqlalchemy和pymysql。
先读取Excel并清理列名,统一改成MySQL表中的英文字段名。之后把日期列转成标准格式,空值转成None,这样写入数据库时会变成NULL而不是空字符串。示例代码如下。
import pandas as pd
from sqlalchemy import create_engine
# 创建数据库连接
engine = create_engine('mysql+pymysql://root:123456@127.0.0.1:3306/erp?charset=utf8mb4')
# 读取Excel工作表
df = pd.read_excel('员工数据.xlsx', sheet_name='Sheet1', dtype={'工号': str})
# 统一列名
df.columns = ['emp_no', 'emp_name', 'hire_date', 'salary']
# 日期清洗:无效日期转为NaT,再格式化
df['hire_date'] = pd.to_datetime(df['hire_date'], errors='coerce').dt.strftime('%Y-%m-%d')
# 空值统一转为None,保证写入MySQL为NULL
df = df.where(pd.notnull(df), None)
# 整表写入,method参数使用multi模式批量插入
df.to_sql('emp_import', con=engine, if_exists='append', index=False, method='multi')
to_sql在数据量较小、列名已经对齐时非常方便,但它生成的SQL有时对MySQL优化不够好。如果数据量达到几十万行,建议改用executemany方式手动拼接插入语句,减少网络往返和事务开销。下面是一个改进版本。
from sqlalchemy import text
# 准备参数列表
rows = [tuple(r) for r in df.itertuples(index=False)]
insert_sql = text(
'INSERT INTO emp_import (emp_no, emp_name, hire_date, salary) '
'VALUES (:emp_no, :emp_name, :hire_date, :salary)'
)
# 在单个事务中批量执行
with engine.begin() as conn:
conn.execute(insert_sql, [
{
'emp_no': r[0],
'emp_name': r[1],
'hire_date': r[2],
'salary': r[3]
}
for r in rows
])
这种方式的优点是可以把清洗逻辑固化在脚本中,定时任务直接调用即可。即使Excel里出现单元格合并、前导零丢失、部门字段混入空格等问题,也可以在写入前统一修复。唯一需要注意的是pandas读取Excel会占用较多内存,超大文件最好分块读取或先转成CSV再处理。
四、方案三:使用图形化工具完成导入
对于不熟悉命令行的用户,Navicat、DBeaver和MySQL Workbench等图形化工具提供了更直观的导入入口。以Navicat为例,右键目标数据库下的表,选择导入向导,再选择Excel文件格式,工具会自动解析工作表。导入过程中可以预览前几行数据,也可以手动调整每一列对应的目标字段。
工具导入的底层逻辑仍然是读取Excel后生成一批SQL语句再执行。它的优势在于减少了人工编辑SQL的成本,适合临时性、一次性的数据迁移。缺点是当数据量很大时,图形界面会卡顿,而且可控性不如脚本方案。如果表结构变化频繁,或者需要从多个Excel文件中按规则抽取数据,还是建议使用Python方案维护。
五、避坑指南:编码、空值与重复键
无论采用哪种方式,编码问题都会首先暴露出来。Excel另存为CSV时,如果系统区域设置不是UTF-8,可能生成GBK编码文件,导入后中文全部变成乱码。因此保存时务必选择CSV UTF-8格式,或者在导入命令中明确指定字符集。数据库连接侧的charset也要使用utf8mb4,否则emoji和部分生僻字会写入失败。
空值处理同样需要提前约定规则。Excel中的空白单元格导入数据库后可能是空字符串,也可能是NULL,这会直接影响后续查询和统计。建议在设计阶段就规定:文本列的空单元格一律转成NULL,数值列的空单元格根据业务转成0或NULL。日期列如果包含历史遗留的非法日期,应该先记录错误日志,而不是让整个批次导入失败。
重复键冲突是导入过程中最常见的异常之一。如果目标表存在主键或唯一索引,而Excel中又有重复行,直接插入会报错。可以在导入前用pandas的drop_duplicates去重,也可以在MySQL侧使用INSERT IGNORE或ON DUPLICATE KEY UPDATE策略。前者忽略重复记录,后者在冲突时更新指定字段,适合做增量同步。
最后要提醒的是,数据库导入操作应尽量放在事务中执行。尤其是批量写入时,如果中途失败,已经插入的数据可能造成脏数据。可以通过合理设置遍历批次大小来控制事务范围,必要时在导入完成后执行SELECT COUNT(*)和抽查若干行,确认行数与Excel数据行一致,以及字段内容没有错位。