2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

UPDATE

UPDATE — 更新表中的行

概要

[ WITH [ RECURSIVE ] with_query [, ...] ]
UPDATE [ ONLY ] table_name [ * ] [ [ AS ] alias ]
    SET { column_name = { expression | DEFAULT } |
          ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |
          ( column_name [, ...] ) = ( sub-SELECT )
        } [, ...]
    [ FROM from_item [, ...] ]
    [ WHERE condition | WHERE CURRENT OF cursor_name ]
    [ RETURNING [ WITH ( { OLD | NEW } AS output_alias [, ...] ) ]
                { * | output_expression [ [ AS ] output_name ] } [, ...] ]

描述

UPDATE 更改表中满足条件的行的指定列的值。只需要在 SET 子句中提及要修改的列;未显式修改的列将保留其先前的值。

有两种方法可以使用数据库中其他表包含的信息来修改表:使用子查询,或在 FROM 子句中指定其他表。哪种技术更合适取决于具体情况。

可选的 RETURNING 子句会导致 UPDATE 在实际更新的每一行基础上计算并返回值。任何使用该表列以及/或 FROM 中提及的其他表的列的表达式都可以被计算。默认情况下,使用该表列的新(更新后)值,但也可以请求旧(更新前)值。 RETURNING 列表的语法与 SELECT 的输出列表的语法完全相同。

您必须拥有该表的 UPDATE 权限,或者至少拥有列出要更新的列的 UPDATE 权限。您还必须拥有对在 expressionscondition 中读取的任何列的 SELECT 权限。

参数

with_query

WITH 子句允许您指定一个或多个可以在 UPDATE 查询中通过名称引用的子查询。有关详细信息,请参阅 第 7.8 节SELECT

table_name

要更新的表的名称(可选模式限定)。如果表名称前指定了 ONLY,则仅在命名的表中更新匹配的行。如果未指定 ONLY,则在继承自命名表的任何表中也会更新匹配的行。可选地,可以在表名后指定 *,以明确指示包含子表。

alias

目标表的替代名称。当提供了别名时,它会完全隐藏表的实际名称。例如,给定 UPDATE foo AS fUPDATE 语句的其余部分必须将此表称为 f,而不是 foo

column_name

table_name 指定的表中的列的名称。列名可以根据需要用子字段名或数组下标限定。在指定目标列时,请勿包含表名 — 例如,UPDATE table_name SET table_name.col = 1 是无效的。

expression

要赋给列的表达式。该表达式可以使用此列和其他列的旧值。

DEFAULT

将列设置为其默认值(如果未为其分配特定的默认表达式,则为 NULL)。标识列将被设置为由关联序列生成的新值。对于生成列,指定此项是允许的,但仅指定了从生成表达式计算该列的正常行为。

sub-SELECT

一个 SELECT 子查询,它生成与前面圆括号括起来的列列表一样多的输出列。子查询在执行时必须只产生一行。如果它产生一行,则其列值将被赋给目标列;如果它不产生行,则将 NULL 值赋给目标列。子查询可以引用要更新的表的当前行的旧值。

from_item

一个表表达式,允许其他表的列出现在 WHERE 条件和更新表达式中。这使用了与 SELECT 语句的 FROM 子句相同的语法;例如,可以为表名指定别名。不要将目标表作为 from_item 重复,除非您打算进行自连接(在这种情况下,它必须在 from_item 中带别名出现)。

condition

一个返回 boolean 类型值的表达式。只有当此表达式返回 true 的行才会被更新。

cursor_name

WHERE CURRENT OF 条件中使用的游标名称。要更新的行是最近从该游标中获取的行。该游标必须是对 UPDATE 的目标表执行的非分组查询。注意 WHERE CURRENT OF 不能与布尔条件一起指定。有关将游标与 WHERE CURRENT OF 一起使用的更多信息,请参阅 DECLARE

output_alias

RETURNING 列表中的 OLDNEW 行的可选替代名称。

默认情况下,可以通过编写 OLD.column_nameOLD.* 返回目标表中的旧值,通过编写 NEW.column_nameNEW.* 返回新值。提供别名时,将隐藏这些名称,并且必须使用别名引用旧行或新行。例如 RETURNING WITH (OLD AS o, NEW AS n) o.*, n.*

output_expression

在更新完每一行后由 UPDATE 命令计算并返回的表达式。该表达式可以使用 table_name 指定的表或 FROM 中列出的表中的任何列名。写入 * 以返回所有列。

列名或 * 可以使用 OLDNEW,或者 OLDNEW 的相应 output_alias 进行限定,以返回旧值或新值。未限定的列名、*,或使用目标表名或别名限定的列名或 * 将返回新值。

output_name

用于返回列的名称。

输出

成功完成后,UPDATE 命令返回一个命令标签,格式为

UPDATE count

count 是更新的行数,包括值未更改的匹配行。请注意,当更新被 BEFORE UPDATE 触发器抑制时,该数字可能小于匹配 condition 的行数。如果 count 为 0,则查询未更新任何行(这不被视为错误)。

如果 UPDATE 命令包含 RETURNING 子句,结果将类似于包含 RETURNING 列表中定义的列和值的 SELECT 语句,该语句在命令更新的行上进行计算。

注释

当存在 FROM 子句时,实际上发生的是目标表与 from_item 列表中的表连接,并且连接的每一输出行代表目标表的一个更新操作。使用 FROM 时,您应确保连接最多为每个要修改的行产生一行输出。换句话说,目标行不应连接到其他表中的多个行。如果这样做,将只使用其中一个连接行来更新目标行,但具体使用哪一个行是不可预测的。

由于这种不确定性,仅在子查询中引用其他表更安全,尽管通常比使用连接更难读且速度更慢。

对于分区表,更新一行可能会导致它不再满足包含分区的分区约束。在这种情况下,如果分区树中存在另一个分区,该行满足其分区约束,则该行将被移动到该分区。如果不存在这样的分区,则会发生错误。在后台,行移动实际上是 DELETEINSERT 操作。

在移动的行上可能发生并发 UPDATEDELETE 操作,从而导致串行化失败错误。假设会话 1 正在对分区键执行 UPDATE,而此时并发会话 2(该行对该会话可见)正在对该行执行 UPDATEDELETE 操作。在这种情况下,会话 2 的 UPDATEDELETE 将检测到行移动并引发串行化失败错误(该错误始终返回 SQLSTATE 代码 '40001')。应用程序可能希望在这种情况下重试事务。在通常情况下(表未分区,或没有行移动),会话 2 将识别出新更新的行,并对这个新的行版本执行 UPDATE/DELETE

请注意,虽然行可以从本地分区移动到外部表分区(前提是外部数据包装器支持元组路由),但它们不能从外部表分区移动到其他分区。

如果外键直接引用源分区的祖先,而该祖先与 UPDATE 查询中提到的祖先不是同一个,那么尝试将行从一个分区移动到另一个分区将失败。

示例

films 表的 kind 列中的单词 Drama 更改为 Dramatic

UPDATE films SET kind = 'Dramatic' WHERE kind = 'Drama';

调整 weather 表中一行数据的温度记录,并将降水量重置为其默认值

UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
  WHERE city = 'San Francisco' AND date = '2003-07-03';

执行相同操作,并返回更新后的条目和旧的降水量值

UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
  WHERE city = 'San Francisco' AND date = '2003-07-03'
  RETURNING temp_lo, temp_hi, prcp, old.prcp AS old_prcp;

使用替代的列列表语法执行相同的更新

UPDATE weather SET (temp_lo, temp_hi, prcp) = (temp_lo+1, temp_lo+15, DEFAULT)
  WHERE city = 'San Francisco' AND date = '2003-07-03';

使用 FROM 子句语法,增加管理 Acme Corporation 客户的销售人员的销售额计数

UPDATE employees SET sales_count = sales_count + 1 FROM accounts
  WHERE accounts.name = 'Acme Corporation'
  AND employees.id = accounts.sales_person;

使用 WHERE 子句中的子查询执行相同的操作

UPDATE employees SET sales_count = sales_count + 1 WHERE id =
  (SELECT sales_person FROM accounts WHERE name = 'Acme Corporation');

更新客户表中的联系人姓名,以匹配当前分配的销售人员

UPDATE accounts SET (contact_first_name, contact_last_name) =
    (SELECT first_name, last_name FROM employees
     WHERE employees.id = accounts.sales_person);

使用连接也可以实现类似的结果

UPDATE accounts SET contact_first_name = first_name,
                    contact_last_name = last_name
  FROM employees WHERE employees.id = accounts.sales_person;

然而,第二个查询可能会产生意外结果,如果 employees.id 不是唯一键,而第一个查询保证会报错(如果有多个 id 匹配)。此外,如果某个 accounts.sales_person 条目没有匹配项,第一个查询会将相应的姓名字段设置为 NULL,而第二个查询则根本不会更新该行。

更新汇总表中的统计信息以匹配当前数据

UPDATE summary s SET (sum_x, sum_y, avg_x, avg_y) =
    (SELECT sum(x), sum(y), avg(x), avg(y) FROM data d
     WHERE d.group_id = s.group_id);

尝试插入新的库存项及其库存数量。如果该项已存在,则更新现有项的库存计数。为了不使整个事务失败,请使用保存点

BEGIN;
-- other operations
SAVEPOINT sp1;
INSERT INTO wines VALUES('Chateau Lafite 2003', '24');
-- Assume the above fails because of a unique key violation,
-- so now we issue these commands:
ROLLBACK TO sp1;
UPDATE wines SET stock = stock + 24 WHERE winename = 'Chateau Lafite 2003';
-- continue with other operations, and eventually
COMMIT;

更改 films 表在游标 c_films 当前定位的行上的 kind

UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films;

影响多行的更新可能会对系统性能产生负面影响,例如表膨胀、复制延迟增加以及锁争用增加。在这种情况下,可以考虑分批执行操作,并在批次之间对表执行 VACUUM 操作。虽然 UPDATE 没有 LIMIT 子句,但可以通过使用 通用表表达式 和自连接来实现类似效果。使用标准的 PostgreSQL 表访问方法,对系统列 ctid 的自连接非常高效

WITH exceeded_max_retries AS (
  SELECT w.ctid FROM work_item AS w
    WHERE w.status = 'active' AND w.num_retries > 10
    ORDER BY w.retry_timestamp
    FOR UPDATE
    LIMIT 5000
)
UPDATE work_item SET status = 'failed'
  FROM exceeded_max_retries AS emr
  WHERE work_item.ctid = emr.ctid;

此命令需要重复执行,直到没有剩余的行需要更新。使用 ORDER BY 子句可以使命令优先更新哪些行;如果其他更新操作使用相同的排序,它还可以防止与其他更新操作发生死锁。如果担心锁争用,则可以在CTE中添加 SKIP LOCKED,以防止多个命令更新同一行。但是,然后需要一个没有 SKIP LOCKEDLIMIT 的最终 UPDATE,以确保没有匹配的行被遗漏。

兼容性

此命令符合SQL标准,除了 FROMRETURNING 子句是 PostgreSQL 扩展,WITHUPDATE 一起使用也是如此。

其他一些数据库系统提供了 FROM 选项,其中目标表应再次列在 FROM 中。 PostgreSQLFROM 的解释并非如此。在移植使用此扩展的应用程序时要小心。

根据标准,目标列名圆括号括起来的子列表的源值可以是产生正确数量列的任何行值表达式。 PostgreSQL 只允许源值为 行构造器 或子 SELECT。在行构造器的情况下,单个列的更新值可以指定为 DEFAULT,但在子 SELECT 中不行。

提交更正

如果您在文档中发现任何不正确、与您对特定功能的经验不符或需要进一步澄清的内容,请使用 此表单 来报告文档问题。