插入数据 INSERT
本教程共 46 篇 · 第 16 篇 · 更新于 2026-07-30 · 约 10 分钟阅读
16. 插入数据 INSERT
本节目标:学会用 INSERT 语句往表里塞数据,从最基础的单行插入到批量插入、从查询结果插入,再到遇到重复主键时的三种处理策略,学完就能应对日常所有的”写数据”场景。
16.1 INSERT 的基本语法
建好了表,里面是空的,怎么往里填数据?就用 INSERT 语句。
最基本的写法是指定列名和对应的值:
INSERT INTO users (username, email, age)
VALUES ('张三', 'zhangsan@example.com', 25);
这句话的意思是:往 users 表里插一行,username 填”张三”,email 填那个邮箱,age 填 25。
列名和值的顺序要一一对应,数量也得一样。id 和 created_at 没写,因为 id 是自增主键会自动生成,created_at 设了默认值(比如 DEFAULT CURRENT_TIMESTAMP)也会自动填充。
执行成功后,MySQL 会返回类似这样的信息:
Query OK, 1 row affected (0.01 sec)
省略列名的写法
你也可以不写列名,直接给所有列的值:
INSERT INTO users
VALUES (NULL, '李四', 'lisi@example.com', 30, '2026-07-30 10:00:00');
但这种写法不推荐。因为你必须按建表时列的顺序,把每个列的值都填上,哪怕有自增列也要写 NULL。一旦以后改了表结构加了列,这条语句就直接报错。
Warning永远写上列名。这是血泪教训——我之前维护过一个老项目,全是省略列名的 INSERT,后来加了字段,几百条 SQL 全炸了。
16.2 多行批量插入
一条一条插太慢了。MySQL 支持一条 INSERT 语句插入多行数据,用逗号分隔:
INSERT INTO users (username, email, age)
VALUES
('张三', 'zhangsan@example.com', 25),
('李四', 'lisi@example.com', 30),
('王五', 'wangwu@example.com', 28),
('赵六', 'zhaoliu@example.com', 22);
一条语句插 4 行,比写 4 条 INSERT 快得多。原因很简单:网络只往返一次,SQL 也只解析一次。
Tip批量插入是提升写入性能的常用手段。导入几千行数据时,别用循环一条条插,拼成一条多行 INSERT 效率能提升十几倍。但单条也别太长,建议每 500-1000 行一批。
16.3 INSERT … SELECT:从查询结果插入
有时候你想把一张表的数据复制到另一张表,或者把查询结果存下来。INSERT ... SELECT 就是干这个的。
-- 把 age 大于 25 的用户插入到一张备份表
INSERT INTO users_backup (username, email, age)
SELECT username, email, age
FROM users
WHERE age > 25;
SELECT 查出来几行,就插入几行。列的数量和类型要跟 INSERT 后面的列对应上。
一个常见用途是给订单表造测试数据:
-- 给每个用户生成一条金额为 100 的待支付订单
INSERT INTO orders (user_id, amount, status)
SELECT id, 100.00, 'pending'
FROM users;
Note
INSERT ... SELECT从同一张表查再插回去也是允许的,但要小心主键冲突。加个WHERE条件过滤一下,别搞出死循环。
16.4 插入日期和时间
日期时间怎么插?其实跟字符串一样,用引号包起来:
INSERT INTO orders (user_id, amount, status, created_at)
VALUES (1, 199.00, 'paid', '2026-07-30 14:30:00');
MySQL 能识别多种日期格式:
| 类型 | 格式 | 示例 |
|---|---|---|
| DATE | YYYY-MM-DD | ’2026-07-30’ |
| TIME | HH:MM:SS | ’14:30:00’ |
| DATETIME | YYYY-MM-DD HH:MM:SS | ’2026-07-30 14:30:00’ |
| TIMESTAMP | YYYY-MM-DD HH:MM:SS | ’2026-07-30 14:30:00’ |
如果你不想手写时间,可以用 MySQL 内置函数:
-- 插入当前时间
INSERT INTO orders (user_id, amount, status, created_at)
VALUES (1, 99.00, 'paid', NOW());
-- 或者用 CURRENT_TIMESTAMP
INSERT INTO orders (user_id, amount, status, created_at)
VALUES (2, 88.00, 'pending', CURRENT_TIMESTAMP);
Tip如果建表时给
created_at设了DEFAULT CURRENT_TIMESTAMP,插入时直接省略这个列就行,MySQL 会自动填当前时间。这是最推荐的做法。
16.5 遇到重复主键怎么办
插入数据时如果主键或唯一索引冲突了,MySQL 默认会直接报错,整条插入失败。针对这种情况,有三种策略可选。
策略一:INSERT IGNORE——冲突就跳过
在 INSERT 后面加 IGNORE 关键字,遇到冲突就跳过这条,不报错:
INSERT IGNORE INTO users (id, username, email, age)
VALUES (1, '张三', 'zhangsan@example.com', 25);
如果 id=1 已经存在,这条不会报错,但也不会插入。返回信息里会提示:
Query OK, 0 rows affected, 1 warning (0.00 sec)
适合场景:批量导入数据,允许部分重复,重复的直接忽略。
策略二:REPLACE——冲突就替换
REPLACE 的用法跟 INSERT 一模一样,但遇到主键冲突时,它会先删掉旧行,再插入新行:
REPLACE INTO users (id, username, email, age)
VALUES (1, '张三更新', 'zhangsan_new@example.com', 26);
Warning
REPLACE是先删后插,意味着旧行的自增值会变,外键关联也可能受影响。如果别的表通过外键引用了这行,删除可能被阻止或触发级联删除。用之前想清楚。
策略三:ON DUPLICATE KEY UPDATE——冲突就更新
这是最灵活的方案。冲突时,不删不跳,而是更新已有行的指定列:
INSERT INTO users (id, username, email, age)
VALUES (1, '张三', 'zhangsan@example.com', 25)
ON DUPLICATE KEY UPDATE age = 25, email = 'zhangsan@example.com';
id=1 如果已存在,就更新它的 age 和 email。如果不存在,就正常插入。
你还可以用 VALUES() 函数引用刚才想插入的值,避免写两遍:
INSERT INTO users (id, username, email, age)
VALUES (1, '张三', 'zhangsan@example.com', 25)
ON DUPLICATE KEY UPDATE age = VALUES(age), email = VALUES(email);
NoteMySQL 8.0.20 起推荐用
AS别名替代VALUES()函数写法,因为VALUES()在未来版本可能废弃。新写法长这样:INSERT INTO users (id, username, email, age) VALUES (1, '张三', 'zhangsan@example.com', 25) AS new ON DUPLICATE KEY UPDATE age = new.age, email = new.email;两种写法在 26.7 中都能用,但新项目建议用新写法。
三种策略对比
| 策略 | 冲突时行为 | 是否报错 | 适用场景 |
|---|---|---|---|
| INSERT IGNORE | 跳过,不插入 | 不报错 | 批量导入,允许忽略重复 |
| REPLACE | 删旧行再插新行 | 不报错 | 需要完整覆盖旧数据 |
| ON DUPLICATE KEY UPDATE | 更新指定列 | 不报错 | 插入或更新(upsert) |
16.6 插入时的常见错误
列值数量不匹配
-- 列写了 3 个,值给了 4 个
INSERT INTO users (username, email, age)
VALUES ('张三', 'zhangsan@example.com', 25, '多余');
报错:Column count doesn't match value count。检查列和值的数量是否一致就行。
违反 NOT NULL 约束
-- username 是 NOT NULL,但没给值
INSERT INTO users (email, age)
VALUES ('zhangsan@example.com', 25);
报错:Field 'username' doesn't have a default value。要么给 username 一个值,要么建表时给它设个默认值。
数据类型不匹配
-- age 是整数类型,给了字符串
INSERT INTO users (username, email, age)
VALUES ('张三', 'zhangsan@example.com', '二十五');
MySQL 会尝试转换,转不了就报警告或截断。别依赖这种隐式转换,数据该是什么类型就给什么类型的值。
Tip做完插入操作后,可以用
SELECT * FROM users;查一下,确认数据确实进去了。养成验证的习惯,别等出问题了才回头查。
16.7 小结
INSERT 是写数据的起点。记住几个要点:始终写列名、批量插入用逗号分隔、从查询结果插入用 INSERT ... SELECT、日期时间用引号或内置函数、遇到主键冲突选 IGNORE/REPLACE/ON DUPLICATE KEY UPDATE 三选一。下一章我们学怎么修改已经插入的数据。