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()在不同时区会存成不同时间。这些不会报错,但数据已失真。
