免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Excel数据导入MySQL全攻略:从CSV到LOAD DATA的实用指南

Excel数据导入MySQL全攻略:从CSV到LOAD DATA的实用指南 做开发这些年“Excel 数据导入 MySQL”这种需求我隔三差五就会碰到一次。领导甩过来一份订单表业务同事导出一份会员明细甲方丢来一个产品清单开场白几乎都一样“你帮忙导一下很快的。”实际上如果没提前理清思路这条路一点都“不快”格式千奇百怪、日期字段乱成麻、中文乱码、导入半路报错任何一个坑都能耗掉一下午。这篇文章就把我实际踩过、也最终沉淀下来的几条导入路线完整拆开讲从最土的文件整理到命令行 LOAD DATA再到图形工具和 Python 脚本全部覆盖并且会重点处理时间格式、编码这类最容易翻车的地方。适合刚入门没多久的 MySQL 使用者也适合被 Excel 导入折磨过一阵子的开发、数据分析同学照着操作基本能跑通。1. 先把思路理顺Excel 到 MySQL 的几条现实路线1.1 为什么“快捷方式”不是一个固定的按钮很多人搜“excel 表格数据导入 mysql 的快捷方式”是想找到一个万能按钮点一下就全部搞定。但真实情况是Excel 文件本身变化太多数据量、表结构、目标库环境都不一样所以不存在唯一的“标准答案”只能选一条适合当前场景的路线。我习惯把导入需求分成三个维度来分析第一数据量有多大几十行、几千行还是几十万上百万行处理逻辑完全不同第二Excel 文件干不干净有没有合并单元格、多余标题、特殊符号、隐藏列这直接决定了要不要先做一次数据清洗第三目标表是什么状态全新空表、已有部分数据、还是生产环境的核心表安全要求也不一样。搞清这三个问题之后再谈“用什么工具导”思路就清晰很多。工具只是解决一部分问题真正决定成败的往往是导入前那几分钟的数据整理。1.2 三条主流导入路线的对比与选型我常用的路线就三条图形化工具导入、命令行 LOAD DATA、程序化脚本导入。它们各有擅长的场景我拿项目里遇到的实际案例来说明。导入路线典型工具适合场景上手难度数据可控程度图形化导入Navicat、MySQL Workbench临时导入、肉眼可见的小表、紧急手工处理低较低很多格式问题被工具掩盖CSV LOAD DATAmysql 命令行干净或已整理好的成批数据、百万级大文件中高字段、编码、日期都能精确控制Python 程序导入pandas pymysql / SQLAlchemy多 Sheet、需要清洗转换、跨表关联、定时重复导入中高最高可以写完整数据处理流程选型时我的优先判断是如果是别人临时发来的一个 Excel几十行、几百行用 Navicat 直接导没问题如果是从报表系统导出的标准表格或者数据量上了万我会先转成 CSV 再走 LOAD DATA只要数据需要经过“加工”比如合并单元格、匹配字典、修正格式、拆分成多张表那就直接上 Python别在图形界面里硬凑。另外有一个观点想强调所谓“快捷方式”不如理解成“一条自己能掌控的稳定路径”。你会发现真正熟练的人不是每次都在换新工具而是固定用一两套方案然后把所有边界情况都摸透了。这也是我写这篇文章的原因——不给你推荐花哨的插件而是把最实用的几条路讲透。2. 准备数据Excel 到 CSV 的处理与三种常见坑2.1 为什么我建议先把 xlsx 转成 CSV 再干活很多人会问Navicat 不是可以直接选 Excel 文件吗确实可以但我强烈建议你先在 Excel 里把文件“另存为 CSV ”尤其是数据量稍大的时候。原因主要有三个。第一CSV 是最通用的纯文本格式MySQL 对它几乎没有兼容性问题导入时能看到完整过程出错了也容易定位。第二直接把 xlsx 喂给某些工具时工具对单元格格式、合并区域、嵌入对象的处理结果不可控而 CSV 只保留“行列值”天然帮你把一些花哨格式过滤掉。第三CSV 文件可以用记事本直接打开导入前肉眼就能确认数据长得什么样减少“我明明看到的是这样导进去却变了”的诡异问题。操作上记住一个关键点Windows 下的 Excel 里“另存为 CSV逗号分隔”和“另存为 CSV UTF-8逗号分隔”是两种不同编码。前者是本地 ANSI 编码国内环境通常就是 GBK/GB2312直接导入 MySQL 时中文大概率乱码后者明确是 UTF-8和 MySQL 默认的 utf8mb4 能无缝配合。我一般首选“CSV UTF-8”这是最省心的一步。2.2 导入前必须做的一次“表格体检”动手导入之前我建议你先花五分钟把 Excel 表格从头到尾看一遍这一步能避免很多后续问题。具体检查这么几项第一删掉多余的标题行、汇总行、空行只留下“一行表头 数据区”。比如有些表第一行是“2024年度销售统计”这种大标题第二行是列名第三行才是数据这种文件如果直接导第一条记录很可能就是那个大标题。第二检查表头列名和目标表字段是否对得上编程里讲究“接口对齐”导入数据也是这个道理列名不一致后面映射起来很痛苦。第三去掉合并单元格合并单元格在 CSV 里会被拆成多个字段容易造成列错位。第四检查数据里有没有换行符、特殊符号如果一个单元格里包含了换行导出 CSV 后这一行的结构会被破坏导进去之后数据全乱。这些检查听着琐碎但能解决八成以上的导入报错。我之前接手过一个供应商表格里面有大量单元格换行导致 CSV 行数比 Excel 实际行数多出一倍一开始我还以为是表结构问题排查很久才发现是源文件里的换行符在捣乱。所以“表格体检”不是浪费时间是在给后面的导入排雷。2.3 时间格式为什么总在导入时出问题时间字段是我见过导入问题里出现频率最高的一类尤其是从 Excel 导出的日期。核心原因在于Excel 里的“日期”在底层其实是一个数字它从 1900 年 1 月 0 日开始递增计数。比如你在单元格里看到的是 2024-01-01但它的真实存储值可能是 45292 这样的数字。所以当你把这个单元格导入 MySQL 时如果 MySQL 的字段是 DATE 或 DATETIME它并不能直接识别这个数字最终结果要么报错要么显示成莫名其妙的 1905 年、1969 年甚至变成 0000-00-00。我一直建议的“土办法”是在 Excel 里先把日期列处理成明确的文本格式比如用公式TEXT(A2,yyyy-mm-dd hh:mm:ss)生成一列新值然后复制、粘贴为值再执行另存为 CSV。这样导进去的时间就是标准字符串MySQL 可直接识别。如果不方便改 Excel也可以在导入时用 SQL 的STR_TO_DATE()函数现场转换后面讲 LOAD DATA 时会给出完整写法。总之时间字段别指望工具“自动聪明地识别”主动把它转成标准字符串是最好懂的解决方案。另外如果 CSV 里的时间是2024/6/30 9:30这种斜杠格式MySQL 也不会自动解析同样需要转换函数。3. 最快的那条路LOAD DATA 命令行实战3.1 最小可用的 LOAD DATA 写法我已经把 Excel 整理成了 CSV UTF-8 文件接下来就是发挥 MySQL 原生导入命令优势的时候。LOAD DATA比逐条 INSERT 快得多几万行数据通常几秒就能导入完成这是最接近“快捷方式”字面含义的一招。假设我有一张销售表CREATE TABLE IF NOT EXISTS sales ( id INT AUTO_INCREMENT PRIMARY KEY, order_id VARCHAR(32) NOT NULL, customer_name VARCHAR(64), amount DECIMAL(10,2), order_date DATETIME );对应的 CSV 文件sales.csv第一行是列名内容大概是order_id,customer_name,amount,order_date SO2024001,张三,188.50,2024-06-30 09:30:00 SO2024002,李四,299.00,2024-06-30 10:15:00然后这样写导入命令LOAD DATA LOCAL INFILE C:/tmp/sales.csv INTO TABLE sales CHARACTER SET utf8mb4 FIELDS TERMINATED BY , ENCLOSED BY LINES TERMINATED BY \n IGNORE 1 ROWS (order_id, customer_name, amount, order_date);FIELDS TERMINATED BY ,表示每列用逗号分隔ENCLOSED BY 表示字段可能被双引号包裹IGNORE 1 ROWS表示跳过第一行表头。执行之前要确认目标表存在CSV 文件路径能访问MySQL 客户端已经开启LOCAL权限。这里有个很常见的坑MySQL 8.0 默认把local_infile关闭了如果你执行时遇到Loading local data is disabled这样的报错可以在服务端执行SET GLOBAL local_infile 1;同时客户端连接时加上--local-infile1参数。我第一次用 MySQL 8.0 时就被这个拦截过还以为是文件路径写错了。3.2 字符集中文乱码的根源与处理乱码大概是 LOAD DATA 时最让人烦躁的问题。明明 CSV 文件用 Excel 打开完全正常但导进 MySQL 就全是问号或者乱码原因几乎都出在字符集不匹配上。场景一你在 Excel 里直接“另存为 CSV逗号分隔”这个文件在 Windows 中文环境下是 ANSI/GBK 编码。导入时如果你不指定CHARACTER SETMySQL 默认按utf8mb4解析GBK 编码的中文字节序列在 utf8mb4 看来就是无效或被误解的字符结果自然乱码。两个解决办法要么回头用“CSV UTF-8”格式另存要么在 LOAD DATA 语句里把CHARACTER SET gbk写清楚。我个人更推荐前者因为文件编码是 UTF-8 的话之后不管换哪个环境都更通用。场景二CSV 文件其实是 UTF-8 编码但带 BOM 头。BOM 是文件开头几个不可见字节导入时会粘到第一列的第一个字段上造成“第一个字段莫名多出一个字符”的假象。用 VS Code 打开文件右下角能看到编码信息如果是 “UTF-8 with BOM”另存为 “UTF-8” 就能去掉 BOM。我之前就遇到过表里所有第一列第一个值都带一个\ufeff前缀查询时肉眼看不出来但把条件一写进去就永远匹配不上。3.3 时间字段兜底LOAD DATA 里的 SET 子句转换前面提到如果 CSV 里的时间不能直接被 MySQL 识别用 LOAD DATA 的SET子句做转换是最优雅的办法。比如 CSV 里时间格式是2024/6/30 9:30而目标字段是 DATETIME直接导入会失败。这时可以先把这一列读入一个自定义变量再用STR_TO_DATE()处理LOAD DATA LOCAL INFILE C:/tmp/orders.csv INTO TABLE orders CHARACTER SET utf8mb4 FIELDS TERMINATED BY , ENCLOSED BY LINES TERMINATED BY \n IGNORE 1 ROWS (order_id, customer_name, order_date, amount) SET order_date STR_TO_DATE(order_date, %Y/%m/%d %H:%i:%s);STR_TO_DATE()的第二个参数是格式串%Y表示四位年份%m表示两位月份%d表示日期%H:%i:%s对应时分秒。它解决的问题本质上和 Excel 里用TEXT()函数是一样的把一个不标准的字符串转换成 MySQL 能识别的日期值。如果你的表格里时间列偶尔有空值还要加上一层保护写成STR_TO_DATE(NULLIF(order_date, ), %Y/%m/%d %H:%i:%s)这样空字符串会被变成 NULL而不是报错。日期格式串的匹配规则很严格2024-06-30必须用%Y-%m-%d换成%Y/%m/%d就会得到 NULL。所以建议导入前先 SELECT 几行出来看看实际显示格式再选对应的格式串别凭感觉猜。4. 图形化操作Workbench 和 Navicat 怎么用才不翻车4.1 MySQL Workbench 导入 Excel 的限制与操作MySQL 自带的 Workbench 可以处理导入任务但有一个限制它的数据导入向导只接受 CSV 或 JSON 文件不接受 xlsx。所以如果你想用 Workbench第一步仍然是“另存为 CSV UTF-8”。操作路径大概是打开 Workbench进入数据库右键目标表选择 “Table Data Import Wizard”然后按提示选择 CSV 文件、目标表做字段映射执行导入。第一次用的同学容易卡在目标表那里如果表不存在向导会提示创建一个新表但自动生成的字段类型往往很保守数字列可能变成 VARCHAR日期列可能变成 TEXT。所以我通常建议提前手动把表结构建好再导入数据这样字段类型完全可控后面不会为了转换类型再返工。Workbench 导入还有一个体验问题几百 MB 的大 CSV 会明显卡顿有时候看起来像死机了。你要么耐心等要么干脆换 LOAD DATA 方案。另外Workbench 默认按 UTF-8 解析文件如果你手头只有 ANSI 编码的 CSV记得先转换编码再导入否则中文会重演乱码戏码。4.2 Navicat 导入向导的完整流程与两个注意点Navicat 是很多人觉得“最方便”的图形化工具因为它可以直接选择 Excel 文件不需要提前转 CSV。它的导入向导步骤一般是选中目标表 → 右键 → 导入向导 → 选择 Excel 文件 → 指定工作表 → 字段映射 → 开始导入。实际操作中我建议你注意两个点。第一如果 Excel 文件里有合并单元格或多余标题请在 Excel 里先删掉这些内容再导不然合并区域产生的空值直接被工具当成 NULL标题行也会被当成一条数据写入表里。第二在字段映射页面手动检查每一列的目标类型特别是日期列。Navicat 这种“智能识别”并不可靠遇到不标准的日期格式经常半路报错或导进一堆 0000-00-00。我的做法是宁可先把日期列处理成文本再在映射时让数据库接收字符串导入完成后用 UPDATE 统一转换成日期这样虽然多了两条 SQL但稳定很多。还有一个建议新表导入前最好先用几行数据做一次“试导入”确认字段映射、日期解析都符合预期再跑全量。不要直接在一个大文件上赌运气一旦中间报错重新清洗、重新导入的成本更高。4.3 图形界面导入的几个坏习惯图形化工具最大的隐患是它把复杂过程包装得太“傻瓜”导致很多人忘记校验结果。我见过不少同事导入完看到“成功”提示就交差结果表里数据数量不对、日期错乱、主键重复等业务发现的时候已经过了很久。我的建议是不管用什么工具导入完成后第一时间执行SELECT COUNT(*)和源 Excel 行数比对再随便抽查几条关键字段看看内容是否合理。对于金额、日期这类敏感数据最好再用SUM、MAX、MIN做一轮统计和 Excel 里的结果对一下。这些小习惯花不了两分钟但能在数据问题恶化前就拦下来。另外不要在业务高峰期直接往生产库的表里导大批数据。哪怕只锁定一张表几分钟也可能影响线上业务。能错峰就错峰不能错峰的至少提前确认表的存储引擎和索引情况避免大量写入造成锁表时间过长。5. 程序化导入Python 清洗批量写入的完整套路5.1 什么时候必须上代码图形化和 LOAD DATA 能覆盖大部分场景但有些情况你会在工具里折腾半天也搞不定Excel 里有多个 Sheet每个 Sheet 要导入不同表某些列需要根据字典表翻译成另一套编码源数据里夹杂合并单元格、重复行、明显脏值必须先清洗再入库还有定时任务每周或每天都要导一次相同格式的 Excel。这个时候就该写代码了我选的是 Python。为什么是 Python首先 pandas 读取 Excel 的能力足够强能保留大部分格式信息其次 Python 生态里连接 MySQL 的方案非常成熟最后代码脚本可复用性强这次处理完的流程下次直接换文件路径就能跑。5.2 一个可以直接复用的 pandas 导入脚本下面这个脚本是我在实际项目中经常用的骨架功能覆盖了“读取 Excel → 清洗 → 写库”的完整流程import pandas as pd from sqlalchemy import create_engine # 读取Excel指定phone列按字符串读防止手机号变科学计数法 df pd.read_excel(sales_data.xlsx, sheet_name2024, dtype{phone: str}) # 清洗列名去掉首尾空格转小写空格替换为下划线 df.columns [str(c).strip().lower().replace( , _) for c in df.columns] # 解析时间字段解析不了的变成NaT df[order_date] pd.to_datetime(df[order_date], errorscoerce) # 删除关键列缺失的行 df df.dropna(subset[order_id]) # 把NaN统一转成None避免写入后出现字符串nan df df.where(pd.notna(df), None) # 连接数据库 engine create_engine( mysqlpymysql://root:your_password127.0.0.1:3306/sales_db?charsetutf8mb4 ) # 批量写入chunksize控制每批条数methodmulti提升插入效率 df.to_sql( sales, conengine, if_existsappend, indexFalse, chunksize5000, methodmulti )这里有几个值得展开说明的细节。第一dtype{phone: str}是关键参数如果这一列被 pandas 自动识别成数字手机号、身份证号这类超长数字就会丢失精度变成 1234E17 之类的结果到库里直接没法用于业务匹配。第二errorscoerce让解析不了的时间变成 NaT接着dropna(subset[order_id])可以删掉完全没意义的行但如果你业务上允许订单时间为空就不要用dropna(howany)否则会把很多正常行误删。第三df.where(pd.notna(df), None)这行是我踩过坑才加的。pandas 默认会把空值写成NaN如果直接 to_sql数据库里就会混入字符串nan排查起来极其难受。使用这个脚本前先用pip install pandas openpyxl pymysql sqlalchemy把依赖装好。如果没有写到非常严格的生产环境sqlalchemy 连库这种模式已经足够稳定了。5.3 大数据量写入的优化思路df.to_sql对于几十万行以内的数据压力不大但如果到了几百万行pandas 的默认写入方式可能会非常慢。它的默认行为是逐行 INSERT虽然上面代码里加了methodmulti和chunksize5000已经有明显改善但还是不如 MySQL 原生的 LOAD DATA 快。我常用的优化思路是“混合模式”先用 pandas 完成所有清洗、格式转换、多表关联等复杂操作最后把清洗结果统一导出成一个临时 CSV 文件再用LOAD DATA LOCAL INFILE灌进 MySQL。这样两头的好处都占了——清洗逻辑写在代码里一目了然数据导入又走了 MySQL 性能最强的路径。Python 脚本里调用 mysql 命令也很简单比如用 subprocess 包装一下import subprocess mysql_cmd ( mysql --local-infile1 -uroot -p -e LOAD DATA LOCAL INFILE \/tmp/clean_sales.csv\ INTO TABLE sales CHARACTER SET utf8mb4 FIELDS TERMINATED BY \,\ ENCLOSED BY \\\\\ LINES TERMINATED BY \\\n\ IGNORE 1 ROWS sales_db ) subprocess.run(mysql_cmd, shellTrue)当数据量上到百万级别时这个组合明显比单纯 pandas to_sql 快很多也少占用不少内存。实测下来同样的数据我用 pandas 逐行写可能要十几分钟走 CSV LOAD DATA 往往几十秒就搞定。量级差距很明显强烈建议你在大文件场景下避开“全用 Python 硬写”的思路。6. 高频报错与我的“导入铁律”6.1 导入常见问题速查表我在文章里讲了很多细节但实际遇到问题时大家还是希望能快速查表解决。下面这个表是我自己整理的高频问题排查清单覆盖了我这几年见过的绝大多数导入异常。现象原因解决办法导入后中文全部是 ??? 或乱码CSV 编码与数据库字符集不一致Excel 另存为 CSV UTF-8LOAD DATA 指定 utf8mb4或指定 CHARACTER SET gbkCSV 导入后第一列第一个值多出看不见的字符文件带 UTF-8 BOM用 VS Code 打开文件另存为“UTF-8”去掉 BOM日期变成 0000-00-00 或 1905 年Excel 日期序列数或斜杠格式未转换Excel 里用 TEXT 转为标准文本或 LOAD DATA 里用 STR_TO_DATE手机号、身份证变成科学计数法整理 Excel 时单元格被格式化成数字先在 Excel 里设为文本列导出 CSV 后再打开确认显示正常导入到一半就报错字段类型不匹配、主键重复或数据超长先查看错误行单独在库里 INSERT 一条复现并修复Loading local data is disabledMySQL 8 默认关闭 local_infile服务端SET GLOBAL local_infile 1;客户端连接加--local-infile1导入后行数比 Excel 多很多单元格内换行符导致 CSV 结构错乱清洗源文件删除单元格内手动换行后再导出 CSV时间导入后多了 8 小时连接驱动与数据库时区不一致在连接 URL 或数据源里显式设置时区或统一处理时间字符串这张表没有覆盖所有边界情况但常见的坑基本都在里面了。遇到没见过的报错我一般会先截取一行数据放到测试表里单独跑这样试错成本最低。6.2 我长期养成的“导入铁律”踩的坑多了自然就总结出几条规矩。我每次做导入不管数据量大小都会强制自己走一遍下面这些流程虽然看着繁琐但能省掉很多事后擦屁股的时间。第一目标表一定要先备份。正式导入前执行CREATE TABLE sales_bak_20240630 AS SELECT * FROM sales;或把原表导出成一个 SQL 文件成本很低但万一导入出错回滚只是换个表名的事。第二先小样后全量。我习惯用LIMIT或把 CSV 文件截断到前几十行先导一次确认字段映射、日期解析、编码都没问题再做全量。第三导入后立即校验数量。源 Excel 里如果数据从第 2 行到第 10001 行那目标表新增的行数就应该是 10000这个比对很简单但能拦截掉大部分低级错误。第四生产库导入前在测试库完整跑一遍。哪怕多花十分钟也远比在生产上出问题再补救安全。6.3 另一个容易被忽略的细节批量重复导入的幂等性很多时候同一个文件可能被“不小心”导入两次结果表里出现重复数据。特别是对接业务文件的时候导入人员换了班次文件没标记清楚就会造成二次执行。常见的兜底方案是业务主键字段建唯一索引用INSERT ... ON DUPLICATE KEY UPDATE或 LOAD DATA 配合IGNORE/REPLACE来控制重复记录。如果表里没有天然的主键就只能靠导入前DELETE指定条件的数据或者做好文件在流程上的“已处理”标记。这个问题虽然不常被提到但实际工作里遇到一次就很头疼建议提前设计好。结尾我现在的默认套路写了这么多最后分享一下我目前实际沉淀下来的导入习惯也算给这篇内容做个自然的收尾。面对一份新的 Excel我先花五分钟看表格结构如果只有几千行、字段规整、编码干净直接另存为 CSV UTF-8然后用一条 LOAD DATA 导入完事如果数据需要合并 Sheet、关联字典、清洗格式就让 pandas 先处理干净再走 CSV LOAD DATA避免在工具里反复纠结只有遇到那种一次性、量特别小、也没有稳定性要求的临时表格我才会打开 Navicat 手工点一遍。这个固定套路不是最炫技的但胜在稳定、可控、自动化程度高已经帮我处理过很多个原本要折腾一下午的“快速导入”需求。如果你现在正好被 Excel 导入 MySQL 的问题卡着我的建议也很简单别急着找一个“万能按钮”先把源文件的编码、日期格式、多余表头这三个问题解决掉再选择一条适合自己的路线执行。数据导入这件事我踩过最多的坑从来不是 MySQL 导不了而是 Excel 那一侧的数据本身太随意。把这层功夫下足后面怎么导都顺。
返回列表