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 { * |output_expression
[ [ AS ]output_name
] } [, ...] ]
UPDATE
修改满足条件的所有行中指定列的值。只有要修改的列才需要在 SET
从句中被提及;未明确修改的列保留它们以前的值。
有两种方法可以使用数据库中其他表中的信息来修改表:使用子查询或在 FROM
从句中指定其他表。哪种技术更合适取决于具体情况。
可选的 RETURNING
从句导致 UPDATE
计算并返回基于实际上更新的每一行的值。可以使用表的列和/或 FROM
中提到的其他表的列的任何表达式进行计算。将使用表的列的新(更新后)值。RETURNING
列表的语法与 SELECT
的输出列表相同。
你必须对表有 UPDATE
权限,或至少对列出要更新的列有权限。你还必须对在 表达式
或 条件
中读取的任何列有 SELECT
权限。
with_query
WITH
从句允许你指定一个或多个子查询,这些子查询可以在 UPDATE
查询中按名称引用。有关详细信息,请参见 第 7.8 节 和 SELECT。
table_name
要更新的表的名称(可选的架构限定)。如果在表名前指定了 ONLY
,则只在命名的表中更新匹配的行。如果未指定 ONLY
,则在继承自命名表的所有表中也会更新匹配的行。或者,可以在表名后指定 *
以明确指示包括后代表。
alias
目标表的替代名称。当提供了别名时,它会完全隐藏表的实际名称。例如,给定 UPDATE foo AS f
,则 UPDATE
语句的剩余部分必须将此表称为 f
,而不是 foo
。
column_name
表名称
指定的表中的一列名称。如有需要,可以对列名称使用子字段名称或数组下标进行限定。目标列指定不应包含表名称 — 例如,UPDATE table_name SET table_name.col = 1
无效。
表达式
分配给列的一个表达式。表达式可以使用此列以及表中其他列的旧值。
DEFAULT
将列设置为其默认值(如果还没有为其指定特定的默认表达式,则为 NULL)。身份列将设置为相关序列生成的新值。对于生成列,允许指定此项,但这仅仅指定从其生成表达式中计算列的通常行为。
子 SELECT
一个 SELECT
子查询,生成与前面括号中列列表中列数一样多的输出列。执行时,子查询最多只能生成一行。如果它生成一行,则其列值被分配给目标列;如果它未生成行,则 NULL 值被分配给目标列。子查询可以引用正在更新的表的当前行的旧值。
from_item
一个表表达式,允许其他表中的列出现在 WHERE
条件和更新表达式中。这使用与 SELECT
语句 FROM
子句相同的语法;例如,可以指定表名称的别名。除非打算自连接(在此情况下,它必须出现在 from_item
中的别名中),否则请勿将目标表重复作为 from_item
。
条件
返回类型为 boolean
的表达式。只有此表达式返回 true
的行才会更新。
cursor_name
在 WHERE CURRENT OF
条件中使用的游标的名称。要更新的行是从此游标最近提取的行。游标必须是针对 UPDATE
' 目标表的非分组查询。请注意,无法将 WHERE CURRENT OF
与布尔条件一起指定。请参见 DECLARE 了解更多信息,了解如何在 WHERE CURRENT OF
中使用游标。
output_expression
一条表达式,由 UPDATE
命令于更新每一行后计算并返回该表达式。该表达式可以使用由 table_name
指明的表中的任意列名或在 FROM
中列出的表。编写 *
以返回所有列。
output_name
用于返回的列的一个名称。
成功执行后,UPDATE
命令返回以下形式的命令标签
UPDATE count
count
是已更新的行数,包括其值未发生改变的匹配行。请注意,该数字可能小于在 BEFORE UPDATE
触发器抑制更新时与 condition
匹配的行数。如果 count
为 0,则查询未更新任何行(这不会被视为错误)。
如果 UPDATE
命令包含 RETURNING
子句,则其结果会与 SELECT
语句类似,其中包含在 RETURNING
列表中定义的列和值,这些值根据命令所更新的行计算而出。
当有 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;
使用替代列列表语法执行相同的更新
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
当前位于的行的 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
子句可以让命令优先处理将更新哪些行;如果其他更新操作使用相同的顺序,它还可以防止与其他更新操作发生死锁。如果锁定争用是一个问题,那么 SKIP LOCKED
可以添加到CTE以防止多个命令更新同一行。但是,这时将需要最终的 UPDATE
,不带 SKIP LOCKED
或 LIMIT
,以确保没有忽略任何匹配行。
此命令符合SQL标准,但 FROM
和 RETURNING
子句是 PostgreSQL 扩展,可以将 WITH
与 UPDATE
结合使用也是如此。
其他一些数据库系统提供 FROM
选项,其中目标表假定再次在 FROM
中列出。这不是 PostgreSQL 解释 FROM
的方式。在移植使用此扩展的应用程序时要小心。
根据标准,带圆括号的目标列名子列表的源值可以是产生正确列数的任何行值表达式。PostgreSQL 仅允许源值是 行构造器 或子 SELECT
。在行构造器的情况下,可以通过 DEFAULT
指定各个列的更新值,但不能在子 SELECT
中指定。