首页 / MySQL 入门教程 / 插入数据 INSERT

MySQL 入门教程

插入数据 INSERT

本教程共 46 篇 · 第 16 篇 · 更新于 2026-07-30 · 约 10 分钟阅读

MySQLMySQL 入门教程INSERTDML数据插入ON DUPLICATE KEY UPDATEINSERT IGNORE

16. 插入数据 INSERT

本节目标:学会用 INSERT 语句往表里塞数据,从最基础的单行插入到批量插入、从查询结果插入,再到遇到重复主键时的三种处理策略,学完就能应对日常所有的”写数据”场景。

16.1 INSERT 的基本语法

建好了表,里面是空的,怎么往里填数据?就用 INSERT 语句。

最基本的写法是指定列名和对应的值:

INSERT INTO users (username, email, age)
VALUES ('张三', 'zhangsan@example.com', 25);

这句话的意思是:往 users 表里插一行,username 填”张三”,email 填那个邮箱,age 填 25。

列名和值的顺序要一一对应,数量也得一样。idcreated_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 能识别多种日期格式:

类型格式示例
DATEYYYY-MM-DD’2026-07-30’
TIMEHH:MM:SS’14:30:00’
DATETIMEYYYY-MM-DD HH:MM:SS’2026-07-30 14:30:00’
TIMESTAMPYYYY-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 如果已存在,就更新它的 ageemail。如果不存在,就正常插入。

你还可以用 VALUES() 函数引用刚才想插入的值,避免写两遍:

INSERT INTO users (id, username, email, age)
VALUES (1, '张三', 'zhangsan@example.com', 25)
ON DUPLICATE KEY UPDATE age = VALUES(age), email = VALUES(email);
Note

MySQL 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 三选一。下一章我们学怎么修改已经插入的数据。