24 INSERT
INSERT 会向表中添加行。基本形式是 INSERT INTO table (columns) VALUES (values)。你可以插入一行、在一条语句中插入多行,或插入 SELECT 的结果。通过单条 INSERT ... VALUES (...), (...), ... 进行批量插入,比执行成千上万条独立语句快得多,因为后者每条语句都要承担一次网络往返和一次重新规划的代价。
本章要点
- 单行和多行
INSERT - 为什么明确列出列名是正确的默认做法
- 从查询插入(
INSERT INTO ... SELECT) - 使用
DEFAULT并省略列 - 状态消息:
INSERT 0 5究竟意味着什么
示例
在 create-table.sql 之后运行此脚本——表必须已经存在。前 3 条语句各插入一行;第 4 条插入两行;第 5 条通过 generate_series 生成并插入三行。psql 输出的状态消息 INSERT 0 5 意味着已插入,旧 OID 为 0,影响 5 行——OID 属于遗留机制,可以忽略第一个数字;第二个数字才是行数。
为什么要列出列名?有两个原因。第一,如果表后来增加一列,那么你的 INSERT INTO fruits VALUES (...)(不带列清单)会失败,而 INSERT INTO fruits (id, name) VALUES (...) 仍然可以工作。译注:这里的“会失败”是原文的概括,并非对所有新增列情形都成立。PostgreSQL 在省略列清单且只提供 N 个值时,会按声明顺序将这些值映射到前 N 列,省略的列使用默认值或 NULL;因此,新增到末尾且可以省略的列未必导致失败,最终还取决于默认值和约束。第二,列出列名能让任何阅读代码的人都清楚意图——值不再是靠位置猜测的谜题。
从文件(CSV 等)批量装载时,优先使用 COPY(后文会介绍)——它的速度明显快于 INSERT。应用代码应将行批量组织到多行 INSERT 语句中,或使用驱动程序级的预备语句。
运行示例
运行方式:
常见陷阱
- 表中已有行时重复运行脚本,会导致主键或唯一约束冲突(添加主键后)。解决办法取决于意图:删除并重新创建表、使用
TRUNCATE,或者使用INSERT ... ON CONFLICT执行插入或更新(UPSERT),详见下一课。 - 按位置执行
INSERT时列顺序错误,会悄悄破坏数据——只要类型兼容,数据库就会把错误的值插入错误的列。始终列出列名。 - 在生产环境中,使用错误时区插入时间戳,是“时间差了 8 小时”的最常见原因。使用
timestamptz,让 PostgreSQL 处理转换。
INSERT INTO t DEFAULT VALUES; 只使用列的默认值插入一行——当每一列都有 DEFAULT 时很有用(例如,id 的值来自序列,created_at 的值来自 now())。
再用 (6, 'fig') 添加一行,然后再次运行文件。(一旦 id 具有唯一约束,这次运行就会失败——我们会在 UPSERT 这一课中修复它。)然后尝试 INSERT INTO fruits (id, name) SELECT n, 'gen_' || n FROM generate_series(200, 205) n;,看看查询驱动的插入方式。
下载
SQL 文件:insert.sql · 全部示例文件 · 准备工作