🎯 本月只剩几天支持 SQLTest.online,帮助我们让 SQL 练习对每个人都免费。 今天捐赠 →
SQL 代码已复制到剪贴板

SQL INSERT INTO:向表中添加行

第 8.1 课 · 阅读时间:约 7 分钟 · 练习题 ↓

SQL INSERT INTO 语句向表中添加新行。在本课中,你将学习 INSERT 的语法、为什么应该始终列出列名、如何一次插入多行、如何用 INSERT INTO ... SELECT 复制数据,以及 NULL、DEFAULT 和自增列是如何填充的。

到目前为止,本课程中的每个查询都是用 SELECT 读取数据。从本模块开始,你将开始修改数据。INSERT、UPDATE 和 DELETE 构成了数据操作语言(DML);其中 INSERT 用于创建新行。

SQLtest 上的示例数据库是只读的,因此在那里执行这些语句会返回权限错误。示例使用 Sakila 模式来展示语法以及真实数据库会强制执行的规则。

SQL INSERT INTO:列列表与 VALUES 列表按位置对应,省略的列使用自增和默认值,以及 INSERT INTO ... SELECT

INSERT 语法

带列列表(推荐)

INSERT INTO table_name (column1, column2, column3)
VALUES (value1, value2, value3);

值与列按位置对应:第一个值进入列出的第一列,依此类推。没有列出的列会得到默认值或 NULL。

不带列列表

INSERT INTO table_name
VALUES (value1, value2, value3, ...);

这种写法必须为每一列提供值,并且顺序要与表中定义列的顺序一致。如果以后有人添加了一列,这条语句就会出错,更糟的是,会把值写入错误的列。在需要保存和重复使用的代码中,请始终列出列名。


插入一行

INSERT INTO actor (first_name, last_name)
VALUES ('JOHNNY', 'DEPP');

结果:新增一位演员。actor_id 自动填充(自增),last_update 从默认值中获得当前时间。

值的规则与 WHERE 中相同:文本和日期用单引号括起来,数字不加引号。


必填列

定义为 NOT NULL 且没有默认值的列必须提供值。在 Sakila 中,customer.create_date 就是这样的列:

-- 错误:Field 'create_date' doesn't have a default value
INSERT INTO customer (store_id, first_name, last_name, email, address_id)
VALUES (1, 'ALICE', 'JOHNSON', 'alice.j@example.com', 5);
-- 正确:必填列有值
INSERT INTO customer (store_id, first_name, last_name, email, address_id, create_date)
VALUES (1, 'ALICE', 'JOHNSON', 'alice.j@example.com', 5, NOW());

结果:1 号门店新增一位客户。customer_id 自动生成,active 得到默认值 1。

值还必须满足外键约束:store_id = 1 和 address_id = 5 必须存在于 store 和 address 表中,否则数据库会拒绝这一行。


插入多行

用逗号分隔的多个值列表可以在一条语句中插入多行:

INSERT INTO actor (first_name, last_name)
VALUES ('TOM', 'HANKS'),
       ('MERYL', 'STREEP'),
       ('LEONARDO', 'DICAPRIO');

结果:新增三位演员。

一条多行语句比多条单行语句快得多:数据库只解析一次,并一次性写入更改。这是批量加载数据的标准方式。


NULL 和 DEFAULT 值

NULL 向允许为空的列写入“没有值”:

INSERT INTO customer (store_id, first_name, last_name, email, address_id, create_date)
VALUES (1, 'BOB', 'SMITH', NULL, 5, NOW());

结果:一位没有电子邮件地址的客户。

省略某一列会用它的默认值填充;如果没有默认值,则为 NULL。

DEFAULT 显式请求默认值,在列出所有列时很方便:

INSERT INTO actor (actor_id, first_name, last_name, last_update)
VALUES (DEFAULT, 'CATE', 'BLANCHETT', DEFAULT);

结果:actor_id 得到下一个自增编号,last_update 得到当前时间。


INSERT INTO ... SELECT

INSERT 也可以不用 VALUES,而是从查询中获取行。表之间复制数据、归档数据或向报表表加载数据时都会这样做:

INSERT INTO target_table (column1, column2, column3)
SELECT source_column1, source_column2, source_column3
FROM source_table
WHERE condition;

SELECT 列表可以组合列、计算结果和常量。例如,如果存在 customer_archive 表,就可以把非活跃客户复制进去:

INSERT INTO customer_archive (customer_id, full_name, archived_at, status_note)
SELECT customer_id,
       CONCAT(first_name, ' ', last_name),
       CURRENT_DATE,
       'Auto-archived'
FROM customer
WHERE active = 0;

结果:15 行,每位非活跃客户一行,包含全名、当天日期和固定的备注。

SELECT 中列的数量和顺序必须与 INSERT 的列列表一致。列名不必相同,但位置必须对应。


常见的 INSERT 错误

错误原因
Column count doesn't match value count值列表与列列表的长度不同
Field '…' doesn't have a default value省略了没有默认值的 NOT NULL 列
Duplicate entry '…' for key 'PRIMARY'该键值已经存在
Cannot add or update a child row: a foreign key constraint fails被引用的行不存在
Incorrect … value值与列的数据类型不匹配

要点:

  • INSERT INTO table (columns) VALUES (values) 添加一行;值按位置与列对应。
  • 始终列出列名:这样语句才能在表结构变化后继续正确工作。
  • 没有默认值的 NOT NULL 列必须提供值;外键必须指向已存在的行。
  • 一条语句可以插入多行:VALUES (...), (...), (...)。
  • INSERT INTO ... SELECT 插入查询的结果。
  • 省略的列得到默认值或 NULL;DEFAULT 关键字显式请求默认值。

常见问题

如何在 SQL 中插入多行?

在 VALUES 后列出多组值,用逗号分隔:INSERT INTO actor (first_name, last_name) VALUES ('TOM', 'HANKS'), ('MERYL', 'STREEP');。这在 MySQL、MariaDB、PostgreSQL、SQL Server 和 SQLite 中都适用。

如何把行从一张表复制到另一张表?

使用 INSERT INTO target (columns) SELECT columns FROM source WHERE condition;。SELECT 返回的列数和顺序必须与目标表的列列表一致。

INSERT 中没有列出的列会怎样?

它们会得到默认值。自增键会得到下一个编号。如果某列没有默认值,它会得到 NULL;如果该列是 NOT NULL,语句就会失败。

尝试解决以下任务,以巩固您在本课中学到的内容。

  1. 添加地址记录
  2. 新员工记录输入
  3. 创建岛屿表