2024年9月26日:PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不支持的版本: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 { * | 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 条件和更新表达式中。这使用与 FROMSELECT 语句相同的语法;例如,可以为表名称指定一个别名。除非您打算进行自连接(在这种情况下,它必须在 from_item 中使用别名出现),否则不要将目标表重复为 from_item

condition

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

cursor_name

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

output_expression

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

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;

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

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;

更改光标 c_films 当前所在行的表 filmskind 列。

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 子句允许命令优先更新哪些行;如果其他更新操作使用相同的排序,它还可以防止死锁。如果锁争用是一个问题,那么可以将 SKIP LOCKED 添加到CTE以防止多个命令更新同一行。但是,然后需要一个最终的 UPDATE,不带 SKIP LOCKEDLIMIT,以确保没有忽略任何匹配的行。

兼容性

此命令符合SQL标准,除了 FROMRETURNING 子句是 PostgreSQL 扩展之外,与 UPDATE 一起使用 WITH 的能力也是如此。

一些其他数据库系统提供了一个 FROM 选项,其中目标表应该在 FROM 中再次列出。这不是 PostgreSQL 解释 FROM 的方式。在移植使用此扩展的应用程序时要小心。

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

提交更正

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