insert语句必须指定列名或严格按建表字段顺序填值;推荐显式列名以避免结构变更导致错误;批量插入用多组values更高效;insert ignore跳过冲突,on duplicate key update支持upsert;insert…select用于从查询结果插入数据。

INSERT 语句的基本写法必须指定列名或按表结构顺序填值
mysql 的 INSERT 不允许只写 VALUES 而不指明列,除非你**严格按建表时的字段顺序、且提供全部非 NULL 默认值字段**。更安全的做法是显式列出列名,避免因表结构变更(比如新增字段、调整顺序)导致插入失败或数据错位。
常见错误现象:column count doesn't match value count at row 1 或 Field 'xxx' doesn't have a default value。
- 推荐写法:
INSERT intO users (name, email, created_at) VALUES ('Alice', 'a@example.com', NOW()); - 危险写法(不推荐):
INSERT INTO users VALUES ('Alice', 'a@example.com', NOW());—— 一旦表加了id INT AUTO_INCREMENT PRIMARY KEY在最前,这条就直接报错 - 如果某字段允许 NULL 或有 DEFAULT,可跳过它,但必须出现在列名列表中才“被跳过”;否则必须显式写
NULL或对应值
批量插入用 VALUES 多组值比循环执行 INSERT 快得多
单条 INSERT 插入 100 行,用 100 次单独语句 vs 1 条带 100 组 VALUES 的语句,性能差距可达 10 倍以上——主要省去了网络往返和语句解析开销。
注意:MySQL 默认 max_allowed_packet 限制单条语句大小(通常 4MB),超限会报错 Packets larger than max_allowed_packet are not allowed。
- 正确批量写法:
INSERT INTO logs (level, msg, time) VALUES ('WARN', 'disk full', '2024-01-01 12:00:00'), ('Error', 'db timeout', '2024-01-01 12:01:00'); - 每组
VALUES之间用英文逗号分隔,不要加分号 - 单次插入行数建议控制在 1000 行以内,兼顾性能与内存/包大小安全
INSERT IGNORE 和 ON DUPLICATE KEY UPDATE 的行为差异很关键
遇到主键或唯一索引冲突时,INSERT IGNORE 直接跳过整行,不报错也不更新;而 ON DUPLICATE KEY UPDATE 允许你指定冲突后更新哪些字段——这是实现“存在则更新、不存在则插入”(upsert)的核心手段。
容易踩的坑:INSERT IGNORE 会静默吞掉其他错误(如字段类型不匹配),不利于调试;REPLACE INTO 实际是先 delete 再 INSERT,会触发自增 ID 变化和外键级联动作,慎用。
- 忽略冲突:
INSERT IGNORE INTO users (id, name) VALUES (1, 'Bob'); - 冲突更新:
INSERT INTO users (id, name, updated_at) VALUES (1, 'Bob', NOW()) ON DUPLICATE KEY UPDATE name = VALUES(name), updated_at = NOW(); -
VALUES(name)表示本次 INSERT 中该字段想设的值,不是当前行旧值
从 SELECT 结果插入数据要用 INSERT … SELECT 语法
想把一张表的数据按条件复制到另一张表(或同一张表),不能用 VALUES,必须用 INSERT ... SELECT。它的字段数量、类型、顺序需与目标列兼容,否则报错。
注意:若 SELECT 返回空结果集,INSERT ... SELECT 什么也不会插入,也不会报错——这点和普通 INSERT 不同,容易误以为执行失败。
- 基础用法:
INSERT INTO archive_logs (level, msg, time) SELECT level, msg, time FROM logs WHERE time - 支持 JOIN、子查询、常量、函数:
INSERT INTO reports (date, total) SELECT CURDATE(), COUNT(*) FROM orders WHERE status = 'paid'; - 目标列名可省略,但前提是 SELECT 字段数、顺序、类型完全匹配目标表定义(含自增、默认值等约束)
实际写 INSERT 时,最容易被忽略的是字符集和时区隐式转换问题:比如客户端连接用 utf8mb4,但表字段是 latin1,插入中文可能变问号;或者 NOW() 在不同时区会存成不同时间。这些不会报错,但数据已失真。