ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Excel数据导入MySQL的三种高效方案与避坑指南

Excel数据导入MySQL的三种高效方案与避坑指南 把Excel数据导进MySQL这个话题看着简单但实战里翻车率极高。我干了十来年数据库和后台开发基本每隔几周就会遇到一次“导入失败”或者“导进去了但数据不对”的情况。这篇文章就是围绕“Excel表格数据导入MySQL的快捷方式”来写把图形化工具、命令行LOAD DATA、脚本化方案三条路完整讲一遍适合刚接触MySQL的人也适合想从“手动导入”升级成“脚本化批量导入”的后端工程师。不管你是要一次性灌测试数据还是要定期把业务Excel同步到线上库看完这篇基本都能找到能直接抄作业的方案。1. 先想明白你要的“快捷”是哪一种快捷1.1 三种典型场景决定了三种导入思路很多人一上来就问“哪种方式导Excel最快”这个问题的前提就错了。导入方案没有绝对的最快只有最适合当前场景的那一种。我这些年经手过的需求大概可以分成三类第一种是一次性导入比如甲方给你一张Excel表让你把历史数据补进某个业务表导完就完事不需要重复执行。这类需求最合适的方案是图形化工具比如Navicat或者MySQL Workbench鼠标点几下就能完成学习成本几乎为零。第二种是周期性同步比如每天早上要把运营导出的一份报表数据同步到MySQL然后跑定时统计。这种场景如果还用图形化工具人就得每天手动打开界面、选文件、点导入既浪费时间又容易漏。正确做法是准备好一个CSV文件然后用LOAD DATA INFILE命令批量插入或者直接写个定时脚本去跑。第三种是复杂清洗型导入Excel里可能有多个Sheet、合并单元格、时间格式乱七八糟、带单位、有空值夹杂这时候直接导入几乎肯定会失败。我的做法是用Python读取Excel用pandas做数据清洗再批量写入MySQL。虽然看起来多了一步但反而最省心。1.2 不同方案的效率对比与选型建议我先给一张简单的选型对照表方便你快速锁定方向方案适用场景上手难度批量速度数据清洗能力Navicat导入向导一次性导入、快速预览低中等弱只做字段映射MySQL Workbench导入向导一次性导入、不装额外软件低中等弱LOAD DATA INFILE批量、重复、CSV格式中极快中可以处理部分格式Python pandas复杂Excel、多Sheet、周期任务高快强任意清洗逻辑这里有个很重要但经常被忽略的道理“快捷”的核心不是导入动作本身有多快而是你花多少时间让数据变得“能导入”。我曾经帮一个朋友处理过一份三千行的Excel里面日期格式有五种手机号有两位是文本格式导致科学计数法还有一列数字带单位。这种数据你就算用LOAD DATA再快也快不起来因为导入之前你得先处理格式。所以选方案的第一步永远是评估你的Excel质量。1.3 不要迷信“一次性全部导入成功”还有一个思维上的坑很多人总觉得导入失败是工具不好用其实大多数失败都是数据本身的问题。比如Excel里常见的“时间”列它本质上是序列号显示出来是2024-01-15底层其实是44941这样的数字。你直接导入MySQL如果目标列是DATETIME类型就会报错或变成0000-00-00。再比如身份证号、银行卡号这类超过15位的长数字Excel会默认转成科学计数法导进去之后精度早就丢了。这些问题不是换一个工具就能解决的必须在前置环节做处理。所以我的建议是不管用什么方案都要先建立“前置校验”的意识。哪怕只是人工把Excel拉一遍也比导入之后才发现数据错乱要省事得多。2. 图形化工具实操一次性导入的速通方案2.1 Navicat导入向导的完整流程与关键细节Navicat是我用得最多的图形化工具主要原因是它对MySQL的兼容性很稳导入过程的报错提示也比Workbench清楚。以Navicat 16为例导入Excel的流程是这样的首先在左侧连接树里找到目标数据库右键点击目标表选择“导入向导”。这一步一定要先选中表因为导航栏里有个“导入”按钮但那是导入整个数据库的别搞混。接着选择文件类型这里有几个选项Excel文件.xls、Excel 2007文件.xlsx、CSV文件。如果你的Excel版本比较新直接选.xlsx就行。然后进入“源文件”步骤选择你要导入的Excel文件。这里要注意一个细节如果你用的是Office新版文件后缀可能是.xlsx但它实际上是个启用宏的文件或者含特殊格式Navicat读取时偶尔会卡住。我的习惯是先把Excel另存为“CSV UTF-8”格式再用Navicat导入CSV兼容性会好很多。当然如果工作表里有复杂公式或合并单元格另存为CSV也能自动把格式拍平。接下来是“定义添加字段”和“选择目标字段”的映射页面。Navicat会显示Excel每一列对应到表里的哪个字段你可以自己调整。这个步骤最容易翻车的点是Excel的列顺序和表的字段顺序不一致时一定要手动把映射关系拉到正确位置别图省事直接下一步。还有如果你要导入的表有自增主键记得在映射时把主键那一列排除掉或者选择“忽略”该字段否则会报主键冲突。最后是“选项”步骤这里有几个必选设置。第一勾选“遇到错误时继续”不要让一条脏数据中断整个导入。第二编码方式选择UTF-8除非你确定Excel内容是全英文。第三如果有时间字段在“日期格式”里指定Excel里的实际格式比如yyyy-MM-dd HH:mm:ss否则很容易出现时间列导入为空的情况。2.2 MySQL Workbench导入向导的替代方案如果你是临时在一台新电脑上处理需求没装Navicat用MySQL Workbench也完全可以。Workbench自带的导入功能藏在菜单栏的“Server” - “Data Import”里但它主要面向SQL文件和CSV直接导入Excel的能力反而不如Navicat顺滑。我一般用Workbench导入Excel的做法是先把Excel另存为CSV然后用Workbench的“Table Data Import Wizard”选择目标表指定CSV文件再做字段映射。这个向导有一个好处是它允许你直接预览前一百行数据导入前就能看出时间格式、空值情况。缺点是它对大文件处理能力一般超过五十万行的话会明显变慢所以我只会在数据量比较小的时候用Workbench。2.3 图形化工具的两个隐藏坑第一个隐藏坑是“本地文件权限”。MySQL服务端默认有一个secure_file_priv参数限制LOAD DATA INFILE只能读取某个指定目录下的文件。图形化工具如果调用的是服务端导入机制也会受这个参数影响。你可能会遇到明明文件路径没问题却报“The MySQL server is running with the --secure-file-priv option”的错误。解决方法是修改my.cnf里的secure_file_priv配置把它指向你的工作目录或者设为空字符串表示不限制改完记得重启MySQL服务。第二个隐藏坑是“Excel单元格内的换行符”。如果某个单元格里有换行导出的CSV里会出现一个被双引号包裹的字段但Excel直接导入时这种换行符会干扰列对齐。我的经验是遇到这种数据优先在Excel里把换行符替换成空格再做导入。这个操作在Excel里的快捷键是CtrlH查找内容输入CtrlJ代表换行符替换为空就能把单元格里的隐藏换行清除真的很实用。3. 命令行与LOAD DATA批量导入的“正规军”3.1 LOAD DATA INFILE基础语法与LOCAL关键字图形化工具虽然方便但数据量一大、需要重复跑的时候还是得靠命令行。MySQL内置的LOAD DATA INFILE是批量导入的王牌方案速度比逐条INSERT快几个数量级。基本语法是LOAD DATA INFILE /tmp/user_data.csv INTO TABLE user CHARACTER SET utf8mb4 FIELDS TERMINATED BY , ENCLOSED BY LINES TERMINATED BY \n IGNORE 1 ROWS;这里面每个参数都有讲究。FIELDS TERMINATED BY ,表示列分隔符是逗号如果你导出的CSV是制表符分隔就改成\t。ENCLOSED BY 表示字段用双引号包裹这个必须和Excel导出CSV时的设置保持一致否则字段里有逗号时会错位。LINES TERMINATED BY \n表示行结束符如果文件是Windows下生成的可能需要写成\r\n。IGNORE 1 ROWS用来跳过CSV的表头行。还有一个非常关键的变体如果你的MySQL跑在远程服务器上而CSV文件在你的本地电脑上需要用LOCAL关键字变成LOAD DATA LOCAL INFILE。这里有个安全层面的说明LOCAL意味着文件从客户端读取然后传输到服务端执行插入这时候服务端不能直接访问你本地文件系统但客户端会把文件内容发过去。这个机制很适合“Excel在本地MySQL在云上”的场景。但要注意MySQL 8.0对LOCAL的支持需要客户端和服务端同时开启local_infile参数否则会报“command not allowed”的错误。3.2 从Excel导出CSV的正确姿势我见过太多人直接在Excel里“另存为CSV”然后拿去LOAD DATA结果导入失败。原因很简单Excel默认保存的CSV是带BOM的UTF-8或者本地编码而且分隔符可能因为系统区域设置变成分号不是逗号。这一节专门讲怎么导出一份“给MySQL吃”的CSV。第一步建议把原始Excel另存一份副本在新的工作簿里操作。第二步清理数据删除多余的表头、合并单元格取消合并并填充、把公式列粘贴为值。第三步选择“文件” - “另存为” - “CSV UTF-8逗号分隔”。注意文件名后缀是.csv编码是UTF-8分隔符是逗号。如果你用的是WPS另存为CSV时编码选项可能不太一样我的经验是优先选“UTF-8”不要选“GBK”。因为MySQL这边如果表结构是utf8mb4你传一个GBK编码的文件进去中文乱码概率极高。除非你确定目标表是GBK编码那才谨慎选择对应的字符集。还有一个细节容易被忽略Excel导出CSV时日期时间列会直接变成类似2024/1/15 14:30的格式而不是标准的2024-01-15 14:30:00。LOAD DATA导入时如果目标列是DATETIMEMySQL也能识别一部分斜杠格式但保险起见我会在Excel里先把日期列用TEXT函数转成标准格式或者用单元格格式设置成yyyy-mm-dd hh:mm:ss后再导出。3.3 用自定义变量处理时间格式和空值LOAD DATA本身不做数据清洗但MySQL给了一个很灵活的扩展可以在导入时定义用户变量然后对变量做函数处理后再插入目标列。这个方法特别适合处理“格式不标准的时间字段”和“空字符串变NULL”这两个高频问题。举个例子你的Excel里时间列是2024/1/15 14:30而表的字段是DATETIME可以直接这样写LOAD DATA LOCAL INFILE /path/data.csv INTO TABLE user_log CHARACTER SET utf8mb4 FIELDS TERMINATED BY , ENCLOSED BY LINES TERMINATED BY \n IGNORE 1 ROWS (login_date, user_name) SET login_time STR_TO_DATE(login_date, %Y/%m/%d %H:%i);这里用到了STR_TO_DATE函数把字符串按指定格式解析成日期。需要说明的是这个函数只是做格式转换并不会验证你给的格式是否和实际数据完全匹配如果Excel里混了几行2024-01-15这样带横杠的格式转换就会返回NULL。所以遇到脏数据时我会先用脚本或者Excel筛选功能把所有时间列格式统一再做LOAD DATA。处理空值也很简单。Excel导出的CSV里空单元格通常会变成一个空字符串也就是连续两个分隔符中间什么都没有。导入时如果不处理MySQL会把空字符串插入到字符串字段看起来像NULL但实际不是。要把它转成真正的NULL可以这么写LOAD DATA LOCAL INFILE /path/data.csv INTO TABLE user CHARACTER SET utf8mb4 FIELDS TERMINATED BY , ENCLOSED BY LINES TERMINATED BY \n IGNORE 1 ROWS (name, phone, email) SET phone NULLIF(phone, ), email NULLIF(email, );NULLIF(expr, )的意思是如果表达式结果等于空字符串就返回NULL否则返回原值。这是我在处理CSV导入时最常用的一个小技巧能省掉大量事后UPDATE操作。3.4 大文件导入的性能参数与执行策略几十万乃至上百万行的数据用LOAAD DATA导入通常也就几秒到十几秒。想让它更快可以在LOAD DATA之前临时改几个参数导入完再改回来。先看这几个SET GLOBAL local_infile 1; SET SESSION bulk_insert_buffer_size 1024 * 1024 * 256; SET SESSION unique_checks 0; SET SESSION foreign_key_checks 0;unique_checks0表示导入时跳过唯一索引校验foreign_key_checks0表示跳过外键校验。这两个临时开关可以显著加快导入速度因为它们减少了每次插入时的索引检查和约束判断。但必须要记住导入完成之后要立刻把这两个参数恢复为1并且手动检查一下数据是否真的满足唯一性和外键约束否则后面跑业务必然踩雷。还有一点如果目标表上有大量索引导入前可以先删掉非必要索引导入后再重建。索引重建的耗时可能比导入本身还长但总时长往往会比“带着索引逐行插入”更短。不过这个操作有一定风险考虑到不是所有读者都能自如处理生产库索引变更我建议只在你自己可控的测试库或明确允许变更的库里这么做。4. 脚本化方案Python pandas的进阶玩法4.1 什么时候必须用脚本而不是图形工具或LOAD DATALOAD DATA虽然快但有个天生短板它只能处理“结构已经规整”的数据。如果你面对的Excel有多级表头、多个Sheet、行内合并单元格、或者需要根据已有数据做逻辑判断后再插入LOAD DATA就只能靠边站。这时候就得让脚本出场。我在实际项目中会用Python的理由大概有这几个第一可以读取.xlsx文件里所有Sheet按需要拼接或者拆分第二可以在内存里做任意清洗操作比如去除首尾空格、类型转换、去重、给缺省值填充默认数据第三可以实现“先查后插”比如判断某个唯一键是否已存在避免重复导入第四可以对接定时任务每天自动执行导入流程不需要人肉操作。4.2 一份可直接改用的pandas导入代码下面这段代码是我经常用来处理Excel导入的模板可以直接复制到本地环境跑。整体思路是用pandas读取Excel做基础清洗再用to_sql方法写入MySQL。需要注意to_sql不是逐条INSERT而是生成批量插入语句效率足够应对十万行级别。import pandas as pd from sqlalchemy import create_engine engine create_engine( mysqlpymysql://root:your_password127.0.0.1:3306/your_db?charsetutf8mb4 ) df pd.read_excel(user_data.xlsx, sheet_name用户表, header0) # 统一列名方便映射到表字段 df.columns [name, phone, email, signup_date] # 清洗去空格、补空值、转换时间格式 df[name] df[name].astype(str).str.strip() df[phone] df[phone].astype(str).str.strip() df[email] df[email].fillna() df[signup_date] pd.to_datetime(df[signup_date], errorscoerce).dt.strftime(%Y-%m-%d %H:%M:%S) # 丢弃解析失败的时间行 df df.dropna(subset[signup_date]) df.to_sql( nameuser, conengine, if_existsappend, indexFalse, chunksize5000 )这段代码有几个容易出问题的地方我逐个说一下。errorscoerce会把无法解析的时间变成NaT也就是空值所以后面用dropna把空时间行丢弃避免把脏数据导入库。chunksize5000是每次批量写入5000行这个值不是越大越好我实测过5000到10000之间对绝大多数MySQL服务器来说是比较舒服的区间再大会导致内存压力和事务过大。另外一个细节pandas会把int64类型的列转成数字写入如果你有手机号这种希望以字符串形式存储的列一定要先astype(str)转换并且注意浮点转字符串时会带.0比如13800138000.0。所以清洗时最好先转成字符串后再做替换把末尾的.0去掉。4.3 多Sheet和多表头数据的处理经验如果你拿到手的Excel不是一个简单的二维表而是带有两个标题行甚至多个Sheet直接用header0是读不出来的。我的处理思路是分两步。第一步先用Python打印Sheet名和每个Sheet的维度搞清楚结构。代码很简单xls pd.ExcelFile(complex.xlsx) print(xls.sheet_names) for sheet in xls.sheet_names: temp pd.read_excel(xls, sheet_namesheet, headerNone, nrows5) print(sheet, temp.shape)看到每个Sheet的结构之后再决定用header0还是header1跳过多余的标题行。第二步如果Sheet是那种“表头在第二行、第一行是合并单元格的大标题”我就先用headerNone把原始数据读进来手动指定列名然后切片选择需要的数据区域。这个方法听起来笨但面对乱七八糟的Excel时反而最稳因为你完全掌控了每一列的位置和名称。如果是需要循环处理多个Sheet并导入同一张表可以在Python里用一个for循环把每个Sheet清洗后的DataFrame追加到同一个列表最后用pd.concat合并再一次性写入。这里建议不要在每个Sheet里单独调一次to_sql因为多次连接数据库会有额外开销而且中途出错时很难回滚。4.4 从几千行到几十万行的性能优化到几十万行的时候pandas的to_sql依然能跑但会明显变慢。主要瓶颈不在pandas而在于SQLAlchemy连接层面的批量写入策略。我的优化经验有三个第一把chunksize调整到10000左右减少交互次数。第二如果服务器内存充足可以把df.to_sql前面的数据读取一次性完成不要边读边写。第三写入之前先把目标表上的非唯一索引全部删掉写入完成后再重建。这个做法规避的是“每插一条都要维护二级索引”的开销效果在大表上非常明显。如果你连pandas都不想依赖还有一个更简单高效的方案把DataFrame导出成CSV然后再用LOAD DATA导入。这样能在极大数据量时保持极高的速度。我经常在流水数据达到百万行时这么做pandas负责清洗导出CSV然后调用MySQL的LOAD DATA命令收尾。两套方案互补基本能覆盖所有场景。5. 常见问题与排查技巧实录5.1 中文乱码和问号问题的根源中文乱码是Excel导入MySQL里出现频率最高的问题没有之一。乱码的原因百分之九十九是编码不一致Excel文件本身是GBK/ANSI编码但MySQL连接或表结构用的是utf8mb4或者反过来。排查思路很简单先确认表结构。执行SHOW CREATE TABLE 表名看字段的字符集是utf8mb4还是gbk。再确认你的导入工具或命令里指定的字符集。Navicat导入向导里有“编码”选项LOAD DATA命令里有CHARACTER SET关键字Python连接串里有charsetutf8mb4。这三个地方的字符集必须和源文件一致或者统一到目标表的字符集。如果是CSV文件还有一个容易被忽略的坑带BOM的UTF-8。Excel的“CSV UTF-8”默认带一个BOM头LOAD DATA读取时会把BOM当成第一个字段的一部分导致第一列出现一个奇怪的字符。解决办法是在命令行里用IGNORE 1 ROWS或者在Python里用utf-8-sig编码读取文件把BOM自动去掉。5.2 时间字段导进去全是0000-00-00这个故障十有八九是Excel里的“日期”本质上是序列号。Excel默认从1900年1月1日开始计算天数日期在底层就是一个整数。显示成2024-01-15只是表现层底层是44941。你导出CSV时如果单元格格式是日期导出的通常是可读字符串但如果单元格格式是常规或者数值导出的就是44941这种数字。MySQL拿到这个数字往DATETIME字段里插自然就会失败或变成0000-00-00。处理办法是在Excel里先把日期列格式化好再导出。选中日期列右键“设置单元格格式”选择“日期”类型选yyyy-mm-dd hh:mm:ss确认数据都变成标准格式后再另存为CSV。如果已经是CSV文件且里面有大量序列号我一般是写个小脚本来转换numpy里可以用pd.to_datetime(44941, unitD, origin1899-12-30)来批量还原注意起点不是1900-01-01而是1899-12-30因为Excel有个著名的1900闰年bug。5.3 唯一键冲突和重复数据怎么处理导入时报Duplicate entry通常是Excel里有重复记录或者你重复导入了同一个文件多次。如果希望重复数据直接跳过而不是中断LOAD DATA里有个IGNORE关键字在INTO TABLE后面加上IGNORE比如LOAD DATA LOCAL INFILE ... IGNORE INTO TABLE user ...遇到唯一键冲突时会跳过这一行导入继续执行。如果你希望重复数据直接覆盖旧记录则用REPLACE关键字。但注意REPLACE本质是删旧插新可能导致自增主键变化而且会触发DELETE相关的触发器。在生产库上使用前一定要想清楚。如果重复数据不是全字段重复只是某一个业务唯一键重复更精细的做法还是走脚本方案在插入前先跑一遍查询判断是否存在再决定是跳过还是更新。这个逻辑用pandas处理非常顺手但带大数据量时查询次数会增加记得用批量IN查询来减少数据库交互。5.4 长数字变科学计数法导致精度丢失身份证号、银行卡号这类超过15位的数字列几乎是Excel导入MySQL的经典翻车点。Excel对超过11位的数字会自动转成科学计数法比如1.38001E17你看到的是这样导出CSV也经常是这样。就算你在Excel里看到的是完整数字另存为CSV时可能就直接变成了科学计数法格式。最可靠的解法是永远不要把这类列当成数字类型。源头处理是在Excel里把这些列的单元格格式设为“文本”再输入或粘贴数据。如果你已经拿到的是被转换过的Excel我的经验是先把该列复制到一个新的工作表使用“数据” - “分列”跳过第一步在第三步选择“文本”强制把这列变成文本格式然后再复制回去。导入到MySQL之后这类列应该用VARCHAR或CHAR类型存储而不是BIGINT因为BIGINT最大只能精确表示19位整数超过就会溢出。身份证号18位理论上BIGINT能放下但后续如果涉及前导零或不可见字符用整型一定会出问题所以一定要用字符串类型。5.5 我后悔没早点养成的三个检查习惯第一导入前永远先备份目标表或者把操作放到测试库。哪怕只是CREATE TABLE user_bak AS SELECT * FROM user;一句话也能让你在导入出错后迅速恢复到干净状态。第二正式导入前只导入前几行做验证。LOAD DATA可以先用一个临时小文件测试或者用LIMIT 1的方式先看效果确认字段映射和时间格式都没问题再全量跑。第三导入后一定要做计数校验Excel的总行数、源文件数据行数、目标表新增行数三个数对不上就一定有问题。我用这三步拦下过太多次脏数据事故成本几乎为零。再分享一个压箱底的小技巧如果Excel列和表字段顺序完全一致LOAD DATA语句里可以不写字段列表直接LOAD DATA ... INTO TABLE 表名MySQL会按顺序把每一列塞进表列。但这个用法对列名和类型极其敏感只建议在你自己十分确定数据结构时用。平时我的习惯永远是显式列出字段列表多打几个字但能防止字段顺序调整后导错列。这个习惯救过我很多次。
返回列表