INSERT 向表中添加行
INSERT 是用来把新行放进表中的语句。它的结构简短而固定:
需要注意三个部分:
INSERT INTO books:目标表。(title, author, year):你要提供值的列。VALUES (...):值本身,顺序与列名列表一致。
id 不在列名列表中,所以 SQLite 会自动为它赋值(它是 INTEGER PRIMARY KEY,会取得 rowid)。你跳过的任何列都会使用它的默认值;如果没有默认值,就是 NULL。
始终列出列名
你可以省略列名列表,按声明顺序为每一列提供值:
-- Works, but fragile:
INSERT INTO books VALUES (NULL, 'Dune', 'Frank Herbert', 1965);
但不要这样做。一旦有人给 books 添加了一列,所有这样的语句要么出错,要么开始把值插进错误的列。把列名写清楚:
明确的列名列表也是一种文档:语句本身就能读懂,不用再去翻找表的定义。
插入多行
列出更多的值元组,就能在一条语句中插入多行:
这比写三条单独的 INSERT 语句更简洁,SQLite 也会把整个操作当作一条语句处理。不过,批量加载时真正的性能提升来自把插入包在事务中,下面就来讲这个。
批量插入:用事务包起来
默认情况下,每条 INSERT 都是一个独立的事务。SQLite 在每个事务结束时都会执行一次 fsync,这才是简单循环插入很慢的原因,而不是插入本身。
把它们组合起来:
一次 fsync,而不是五次。对于成千上万行,差距可能达到两到三个数量级。如果中间有任何一步失败,ROLLBACK 会撤销整批操作。
这就是批量插入的标准做法。无论你是从 Python、Node 还是 Rust 调用 SQLite,都要用 BEGIN / COMMIT 把循环包起来。
INSERT ... SELECT:从另一张表复制
你可以用查询结果而不是字面值来填充表:
SELECT 的列按位置与 INSERT 的列名列表对应。名字不必相同,顺序必须一致。这是归档行、构建报表用的表,或在迁移时复制部分数据的标准做法。
DEFAULT VALUES 与跳过的列
如果某一列有 DEFAULT 子句,你可以把它从列名列表中省略,SQLite 会填入默认值:
created_at 得到了当前时间戳,因为我们没有提供它的值。如果你想要一整行都由默认值组成(适合用作占位行),就用 DEFAULT VALUES 的形式:
新增了两行,value 都是 0,id 也都是自动分配的。
INSERT OR IGNORE:跳过重复行
当一行会违反 UNIQUE 或 PRIMARY KEY 约束时,默认行为是中止语句并报错:
Error: UNIQUE constraint failed: users.email
INSERT OR IGNORE 把这种行为换成“悄悄跳过有问题的那一行”:
留下了三行。重复的那一行被丢弃,没有报错。对于简单的初始化数据,这是在 SQLite 中表达“不存在才插入”的惯用写法:不需要先单独 SELECT 检查,也不需要异常处理。
INSERT OR REPLACE:覆盖重复行
INSERT OR REPLACE 会删除冲突的行,并在原处插入新行:
有一点要小心:REPLACE 是 DELETE 加 INSERT,而不是 UPDATE。如果被删除的行有设置了 ON DELETE CASCADE 的外键指向它,这些子行也会被删除。而且新 INSERT 中没有列出的列都会重置为默认值,不会保留原来的值。
对于大多数“存在就更新,不存在就插入”的情况,你真正需要的是用 ON CONFLICT ... DO UPDATE 实现的 upsert。它有单独的一页介绍。
快速回顾
INSERT INTO table (cols) VALUES (...):基本形式。始终列出列名。- 一次插入多行时,在
VALUES后面写用逗号分隔的元组。 - 真正的批量加载,要用
BEGIN/COMMIT把插入包起来。 INSERT INTO ... SELECT ...从查询结果中复制行。DEFAULT VALUES只用默认值生成一行;被跳过的列也会使用各自的默认值。INSERT OR IGNORE跳过冲突的行;INSERT OR REPLACE覆盖它们(通过先删除再插入)。
下一篇:UPDATE
插入行只是故事的一半。另一半是修改已经存在的行:给计数器加一、修正错别字、把订单标记为已发货。这就是 UPDATE,它也有一套值得养成的好习惯(尤其是 WHERE 子句)。下一篇就会讲到。
常见问题
SQLite 中如何插入一行?
使用 INSERT INTO table (col1, col2) VALUES (val1, val2);。列出列名是可选的,但强烈推荐:这样即使表以后新增了列,语句也能照常工作。不写列名列表时,你必须按声明顺序为每一列提供值。
SQLite 中如何一次插入多行?
在 VALUES 后面写多个用括号括起来的元组,用逗号分隔:INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);。真正的批量加载(成千上万行)要用 BEGIN 和 COMMIT 把插入包在一个事务中:提速来自事务,而不是多行语法本身。
SQLite 中 INSERT OR IGNORE 有什么作用?
INSERT OR IGNORE 会跳过违反 UNIQUE、PRIMARY KEY 或 NOT NULL 约束的行,而不是报错。冲突的行被悄悄丢弃,语句的其余部分继续执行。当你想要“不存在才插入”的效果,又不想单独检查是否存在时,就用它。
为什么 INSERT 时会报“UNIQUE constraint failed”?
SQLite 发现 UNIQUE 或 PRIMARY KEY 列中已经有一行的值与之相同。要么这个值确实重复了,要么你在重复运行初始化数据的脚本。改用 INSERT OR IGNORE 跳过重复行,用 INSERT OR REPLACE 覆盖它们,或者用 ON CONFLICT ... DO UPDATE(upsert)进行更精细的控制。