在 SQL 中,INSERT语句是用于向数据库表中添加新数据的核心语句。它能够灵活适配不同的插入需求,无论是插入单条完整数据、部分字段数据,还是将查询结果直接插入表中,都可以通过INSERT语句实现。本 章将详细讲解INSERT语句的多种用法,并结合实例帮助大家快速掌握。
INSERT 语句的基本语法框架如下,不同用法会在此基础上进行调整:
INSERT INTO 表名 [字段列表] VALUES (值列表); -- 或用于插入查询结果 INSERT INTO 表名 [字段列表] SELECT 查询语句;
说明:
[](方括号)中的内容为可选项。
表名指定要插入数据的目标表。
字段列表指定要插入数据对应的字段。
值列表需与字段列表的顺序、数据类型完全匹配;注意,当插入查询结果时,查询结果的字段数量、数据类型也需与目标表的对应字段一致。
简单示例:
INSERT INTO user(name,gender,age,phone,email,address,create_time,update_time)
VALUES('TEST','男',21,'13800138901', 'test@example.com', '成都市天府大道27号', now(), now());执行 SQL,如下:
> INSERT INTO user(name,gender,age,phone,email,address,create_time,update_time)
VALUES('TEST','男',21,'13800138901', 'test@example.com', '成都市天府大道27号', now(), now())
1 row(s) modified.使用 SELECT 查询插入数据,如下:
> select * from user where name='TEST' user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+----------------+----------+-------------------+-------------------+ 11|TEST|男 | 21|13800138901|test@example.com|成都市天府大道27号|2026-01-05 03:42:42|2026-01-05 03:42:42| 1 row(s) fetched.
为方便后续实例讲解,后续示例将基于前面创建的 user(用户表)进行操作,用户表结构如下:
CREATE TABLE `user` (
`user_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '用户唯一ID',
`name` varchar(50) NOT NULL COMMENT '姓名',
`gender` enum('男','女','未知') DEFAULT '未知' COMMENT '性别',
`age` tinyint unsigned DEFAULT NULL COMMENT '年龄',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`address` varchar(255) DEFAULT NULL COMMENT '地址',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`user_id`),
UNIQUE KEY `phone` (`phone`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';
插入完整的行是最常见的插入场景,指的是向表中所有字段都插入对应的值。实现方式有两种:指定所有字段名,或不指定字段名(需按表字段顺序插入)。
该种方式语法非常明确,插入时在字段列表指定表的所有字段,该种方式优点是即使表结构发生字段顺序调整,插入语句依然能正常执行,推荐优先使用。例如:
> INSERT INTO user(user_id,name,gender,age,phone,email,address,create_time,update_time) VALUES(12,'TEST2','男',21,'13800138902', 'test2@example.com', '成都市天府大道27号', now(), now()) 1 row(s) modified.
上面 SQL 语句,向 user 表的 user_id、name、gender、age、phone、email、address、create_time、update_time 字段分别插入对应的值。注意:由于 user_id 是自增主键,后续插入时可省略该字段,由数据库自动生成自增值。
也可以不指定字段列表,但是 VALUES 后的值列表必须严格按照表的字段顺序(即创建表时的字段顺序)插入,且不能遗漏任何字段(允许为可空字段插入NULL)。例如:
> INSERT INTO user VALUES(13,'TEST3','男',21,'13800138903', 'test3@example.com', '成都市天府大道27号', now(), now()) 1 row(s) modified.
注意:这种方式灵活性差,若后续表中添加、删除或调整字段顺序,该插入语句会直接报错,因此仅适用于表结构固定不变的简单场景。
当表中存在可为空的字段(即未设置 NOT NULL 约束)或自增字段时可以跳过该种字段,可只插入不为空的字段值。注意,未插入的字段会自动填充默认值(如自增字段自动生成值、可空字段填充NULL、设置了默认值的字段填充默认值)。
注意:必须在 INSERT 语句中明确指定要插入的字段名,且 VALUES 的值列表需与指定的字段名顺序、数据类型匹配。
由于 user 表的 user_id 是自增主键,插入时可省略该字段,数据库会自动为其分配唯一的自增值:
> INSERT INTO user(name,gender,age,phone,email,address,create_time,update_time)
VALUES('TEST4','男',21,'13800138904', 'test4@example.com', '成都市天府大道27号', now(), now())
1 row(s) modified.上面 SQL 语句执行后,user 表中会新增一条记录,如下:
> select * from user where name='TEST4' user_id|name |gender|age|phone |email |address |create_time |update_time | -------+-----+------+---+-----------+-----------------+----------+-------------------+-------------------+ 14|TEST4|男 | 21|13800138904|test4@example.com|成都市天府大道27号|2026-01-05 05:52:19|2026-01-05 05:52:19| 1 row(s) fetched.
若某个字段允许为NULL,插入时可直接省略该字段,也可主动插入NULL。在 user 表中,user_id、name、create_time 和 update_time 字段均不能为空,但是 user_id 是自增主键也可以省略,因此最简的插入 SQL 语句如下:
> INSERT INTO user(name,create_time,update_time) VALUES ('TEST5', now(), now())
1 row(s) modified.若字段设置了默认值(如将 gender 的默认值设为 '未知'),插入时省略该字段会自动填充默认值,例如:
> INSERT INTO user(name,create_time,update_time) VALUES ('TEST7', now(), now())
1 row(s) modified.执行后,新增记录如下:
> select * from user where name='TEST7' user_id|name |gender|age|phone|email|address|create_time |update_time | -------+-----+------+---+-----+-----+-------+-------------------+-------------------+ 17|TEST7|未知 | | | | |2026-01-05 06:02:30|2026-01-05 06:02:30| 1 row(s) fetched.
注意,gender 字段使用的默认值 “未知”。
在实际开发中,经常需要将一个表的查询结果直接插入到另一个表中(即 “插入检索结果”),此时可使用INSERT ... SELECT语句,无需手动逐个输入值,高效便捷。
语法如下:
INSERT INTO 目标表名 (目标字段列表) SELECT 源查询字段列表 FROM 源表名 [WHERE 条件];
关键要求:
(1)目标字段列表与源查询字段列表的数量必须一致。
(2) 对应字段的数据类型必须兼容(可自动转换即可)。
(3)无需使用 VALUES 关键字,直接跟随 SELECT 查询语句。
为了演示后续实例,将复制 user 表的结构,创建 user_bak 表,结构与 user 表一致:
CREATE TABLE `user_bak` (
`user_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '用户唯一ID',
`name` varchar(50) NOT NULL COMMENT '姓名',
`gender` enum('男','女','未知') DEFAULT '未知' COMMENT '性别',
`age` tinyint unsigned DEFAULT NULL COMMENT '年龄',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`address` varchar(255) DEFAULT NULL COMMENT '地址',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`user_id`)
) COMMENT='用户表备份';实例:将查询结果插入现有表,将 user 表中年龄大于 30 岁的用户信息插入到 user_bak 表中:
> INSERT INTO user_bak(user_id,name,gender,age,phone,email,address,create_time,update_time) SELECT user_id,name,gender,age,phone,email,address,create_time,update_time FROM user WHERE age>30 3 row(s) modified.
上面 SQL 执行后,user_bak 表中会多出 3 条记录,如下:
> SELECT * FROM user_bak user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+-----------------+-------------+-------------------+-------------------+ 5|孙七 |男 | 35|13800138004| |杭州市西湖区西湖路58号 |2025-01-10 10:38:00|2025-12-31 03:18:07| 7|吴九 | | 32|13800138006|wujiu@example.com|重庆市渝中区解放碑路18号|2025-01-02 01:26:00|2025-12-31 03:18:07| 10|孙十二 |女 | 31|13800138009| |南京市玄武区玄武湖路78号|2025-01-07 18:32:00|2025-12-31 03:18:07| 3 row(s) fetched.
除了向现有表插入检索结果,有时还需要直接复制一个表的结构和数据,创建一个新表(即 “复制表”)。这种场景本质上也是 “插入检索数据” 的延伸,只是目标表是新创建的,而非已存在的表。
语法如下(不同数据库略有差异,以下以 MySQL 为例):
CREATE TABLE 新表名 SELECT 字段列表 FROM 源表名 [WHERE 条件];
字段说明:
(1)该语句会先创建新表名指定的新表,表结构由 SELECT 后的字段列表决定。
(2)若未指定 WHERE 条件,会复制源表的所有数据;如果指定条件,仅复制满足条件的数据。
(3)源表的主键、外键、索引等约束不会自动复制到新表,若需要这些约束,需创建新表后手动添加。
实例1:完整复制表结构和数据,复制 user 表的所有结构和数据,创建 user_copy 表,例如:
> CREATE TABLE user_copy SELECT * FROM user 17 row(s) modified. > SELECT * FROM user_copy user_id|name |gender|age|phone |email |address |create_time |update_time | -------+-----+------+---+-----------+---------------------+---------------+-------------------+-------------------+ 1|张三 |男 | 25|13800138000|zhangsan@example.com |北京市朝阳区建国路88号 |2025-01-18 02:03:00|2025-12-31 03:18:07| ... 14|TEST4|男 | 21|13800138904|test4@example.com |成都市天府大道27号 |2026-01-05 05:52:19|2026-01-05 05:52:19| 15|TEST5|未知 | | | | |2026-01-05 05:58:39|2026-01-05 05:58:39| 16|TEST6|未知 | | | | |2026-01-05 06:00:16|2026-01-05 06:00:16| 17|TEST7|未知 | | | | |2026-01-05 06:02:30|2026-01-05 06:02:30| 17 row(s) fetched.
执行后,user_copy 表会拥有与 user 表完全相同的数据。注意:user 表中 user_id 字段的自增约束不会被复制,user_copy 表的 user_id 字段仅为普通 INT 字段。
实例2:复制表的部分字段和数据,仅复制 user 表的 user_id、name、age 字段信息到 user_copy2 表,如下:
> CREATE TABLE user_copy2 SELECT user_id, name, age FROM user WHERE age>30 3 row(s) modified. -- 查询复制结果 > SELECT * FROM user_copy2 user_id|name|age| -------+----+---+ 5|孙七 | 35| 7|吴九 | 32| 10|孙十二 | 31| 3 row(s) fetched.
执行后,user_copy2 表仅包含 user_id、name、age 三个字段,且数据仅为年龄大于 30 的用户。