MySQL批量插入有四种方式:1. INSERT INTO VALUES适用于小量已知数据;2. INSERT INTO SELECT用于跨表导入;3. INSERT IGNORE/ON DUPLICATE KEY UPDATE处理重复键;4. LOAD DATA INFILE最快,适合百万级本地文件导入。
最基础的写法,适合已知全部字段值、数据量不大的场景。注意字段顺序必须和表定义一致,或显式指定列名避免出错。
常见错误:列数和值数量不匹配,报错 Column count doesn't match value count;插入 NULL 到 NOT NULL 字段导致失败。
INSERT 更快,MySQL 会合并为一个事务批次处理max_allowed_packet 限制INSERT INTO users (name, email, created_at)
VALUES
('Alice', 'alice@example.com', NOW()),
('Bob', 'bob@example.com', NOW()),
('Charlie', 'charlie@example.com', NOW());
适用于迁移、归档、统计汇总等场景,本质是把 SELECT 查询结果作为数据源插入目标表。
关键点:目标表字段类型、长度、约束需兼容查询结果;若目标表有自增主键,SELECT 中通常不提供该列(除非显式设为 NULL 或具体值并关闭 sql_mode=NO_AUTO_VALUE_ON_ZERO)。
INSERT 触发器WHERE、JOIN、聚合函数使用,但要注意目标列数与 SELECT 列数严格一致INSERT INTO user_archive(id, name, email, archived_at) SELECT id, name, email, NOW() FROM users WHERE last_login < DATE_SUB(NOW(), INTERVAL 2 YEAR);
当插入数据可能违反 UNIQUE 约束或主键时,两者策略不同:INSERT IGNORE 直接跳过冲突行;ON DUPLICATE KEY UPDATE 则更新已有记录。
容易忽略的坑:IGNORE 会静默吞掉所有警告(包括非主键冲突的其他问题),不利于排查;UPDATE 子句中不能引用被更新行的旧值做计算(如 count = count + 1 是合法的,但 updated_at = old.updated_at 不行)。
INSERT IGNORE 返回的 affected_rows 为 0 表示跳过,1 表示插入成功,2 表示“本应插入但因重复被忽略”(仅部分 MySQL 版本)ON DUPLICATE KEY UPDATE 只响应定义了 UNIQUE 或 PRIMARY KEY 的列冲突,普通索引无效UPDATE,需确认业务逻辑是否预期如此INSERT INTO stats (date, page, views)
VALUES ('2025-06-01', '/home', 120)
ON DUPLICATE KEY UPDATE views = views + 1;
这是 MySQL 原生最快的批量插入方式,适合从 CSV、TSV 等文本文件导入数十万行以上数据。比拼接 SQL 插入快 20 倍以上。
权限和路径限制最多:需要 FILE 权限;文件必须位于数据库服务器本地(不是客户端);默认只允许 /var/lib/mysql-files/ 等白名单目录(由 secure_file_priv 控制)。
SHOW VARIABLES LIKE 'secure_file_priv' 查看允许路径SET FOREIGN_KEY_CHECKS = 0),否则速度骤降LOAD DATA INFILE '/var/lib/mysql-files/users.csv' INTO TABLE users FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS (name, email, @dummy_created_at) SET created_at = STR_TO_DATE(@dummy_created_at, '%Y-%m-%d %H:%i:%s');实际用哪一种,取决于数据来源、规模、是否需去重、以及你有没有服务器文件访问权限。小量手工插入用
VALUES,跨表搬运用 SELECT,高频写入带幂等性要求用 ON DUPLICATE KEY UPDATE,而百万级离线导入几乎只能靠 LOAD DATA INFILE —— 其他方式在性能和稳定性上都撑不住。