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
) } [, ...] [ FROMfrom_item
[, ...] ] [ WHEREcondition
| WHERE CURRENT OFcursor_name
] [ RETURNING [ WITH ( { OLD | NEW } ASoutput_alias
[, ...] ) ] { * |output_expression
[ [ AS ]output_name
] } [, ...] ]
UPDATE
更改表中满足条件的行的指定列的值。只需要在 SET
子句中提及要修改的列;未显式修改的列将保留其先前的值。
有两种方法可以使用数据库中其他表包含的信息来修改表:使用子查询,或在 FROM
子句中指定其他表。哪种技术更合适取决于具体情况。
可选的 RETURNING
子句会导致 UPDATE
在实际更新的每一行基础上计算并返回值。任何使用该表列以及/或 FROM
中提及的其他表的列的表达式都可以被计算。默认情况下,使用该表列的新(更新后)值,但也可以请求旧(更新前)值。 RETURNING
列表的语法与 SELECT
的输出列表的语法完全相同。
您必须拥有该表的 UPDATE
权限,或者至少拥有列出要更新的列的 UPDATE
权限。您还必须拥有对在 expressions
或 condition
中读取的任何列的 SELECT
权限。
with_query
WITH
子句允许您指定一个或多个可以在 UPDATE
查询中通过名称引用的子查询。有关详细信息,请参阅 第 7.8 节 和 SELECT。
table_name
要更新的表的名称(可选模式限定)。如果表名称前指定了 ONLY
,则仅在命名的表中更新匹配的行。如果未指定 ONLY
,则在继承自命名表的任何表中也会更新匹配的行。可选地,可以在表名后指定 *
,以明确指示包含子表。
alias
目标表的替代名称。当提供了别名时,它会完全隐藏表的实际名称。例如,给定 UPDATE foo AS f
,UPDATE
语句的其余部分必须将此表称为 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
列表中的 OLD
或 NEW
行的可选替代名称。
默认情况下,可以通过编写 OLD.
或 column_name
OLD.*
返回目标表中的旧值,通过编写 NEW.
或 column_name
NEW.*
返回新值。提供别名时,将隐藏这些名称,并且必须使用别名引用旧行或新行。例如 RETURNING WITH (OLD AS o, NEW AS n) o.*, n.*
。
output_expression
在更新完每一行后由 UPDATE
命令计算并返回的表达式。该表达式可以使用 table_name
指定的表或 FROM
中列出的表中的任何列名。写入 *
以返回所有列。
列名或 *
可以使用 OLD
或 NEW
,或者 OLD
或 NEW
的相应 output_alias
进行限定,以返回旧值或新值。未限定的列名、*
,或使用目标表名或别名限定的列名或 *
将返回新值。
output_name
用于返回列的名称。
成功完成后,UPDATE
命令返回一个命令标签,格式为
UPDATE count
count
是更新的行数,包括值未更改的匹配行。请注意,当更新被 BEFORE UPDATE
触发器抑制时,该数字可能小于匹配 condition
的行数。如果 count
为 0,则查询未更新任何行(这不被视为错误)。
如果 UPDATE
命令包含 RETURNING
子句,结果将类似于包含 RETURNING
列表中定义的列和值的 SELECT
语句,该语句在命令更新的行上进行计算。
当存在 FROM
子句时,实际上发生的是目标表与 from_item
列表中的表连接,并且连接的每一输出行代表目标表的一个更新操作。使用 FROM
时,您应确保连接最多为每个要修改的行产生一行输出。换句话说,目标行不应连接到其他表中的多个行。如果这样做,将只使用其中一个连接行来更新目标行,但具体使用哪一个行是不可预测的。
由于这种不确定性,仅在子查询中引用其他表更安全,尽管通常比使用连接更难读且速度更慢。
对于分区表,更新一行可能会导致它不再满足包含分区的分区约束。在这种情况下,如果分区树中存在另一个分区,该行满足其分区约束,则该行将被移动到该分区。如果不存在这样的分区,则会发生错误。在后台,行移动实际上是 DELETE
和 INSERT
操作。
在移动的行上可能发生并发 UPDATE
或 DELETE
操作,从而导致串行化失败错误。假设会话 1 正在对分区键执行 UPDATE
,而此时并发会话 2(该行对该会话可见)正在对该行执行 UPDATE
或 DELETE
操作。在这种情况下,会话 2 的 UPDATE
或 DELETE
将检测到行移动并引发串行化失败错误(该错误始终返回 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 LOCKED
或 LIMIT
的最终 UPDATE
,以确保没有匹配的行被遗漏。
此命令符合SQL标准,除了 FROM
和 RETURNING
子句是 PostgreSQL 扩展,WITH
与 UPDATE
一起使用也是如此。
其他一些数据库系统提供了 FROM
选项,其中目标表应再次列在 FROM
中。 PostgreSQL 的 FROM
的解释并非如此。在移植使用此扩展的应用程序时要小心。
根据标准,目标列名圆括号括起来的子列表的源值可以是产生正确数量列的任何行值表达式。 PostgreSQL 只允许源值为 行构造器 或子 SELECT
。在行构造器的情况下,单个列的更新值可以指定为 DEFAULT
,但在子 SELECT
中不行。
如果您在文档中发现任何不正确、与您对特定功能的经验不符或需要进一步澄清的内容,请使用 此表单 来报告文档问题。