# 更新

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 ] [, ...] ]

# 描述

更新更改满足条件的所有行中指定列的值。只有需要修改的列需要在条款;未显式修改的列保留其先前的值。

使用数据库中其他表中包含的信息来修改表有两种方法:使用子选择,或在条款。哪种技术更合适取​​决于具体情况。

可选的返回条款原因更新根据实际更新的每一行计算和返回值。使用表的列和/或中提到的其他表的列的任何表达式, 可以计算。使用表列的新(更新后)值。的语法返回列表与输出列表的列表相同选择.

你必须拥有更新表或至少列出要更新的列的特权。您还必须拥有选择对其值在*表达式要么健康)状况*.

# 参数

with_query

子句允许您指定一个或多个子查询,这些子查询可以在更新询问。看第 7.8 节选择详情。

表名

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

别名

目标表的替代名称。提供别名时,它会完全隐藏表的实际名称。例如,给定更新 foo AS f,其余的更新声明必须将此表称为f不是.

列名

表中的列名*表名*.如果需要,列名可以用子字段名或数组下标限定。不要在目标列的规范中包含表的名称——例如,更新表名 SET 表名.col = 1是无效的。

表达

分配给列的表达式。表达式可以使用表中此列和其他列的旧值。

默认

将列设置为其默认值(如果没有为其分配特定的默认表达式,则该列将为 NULL)。标识列将设置为关联序列生成的新值。对于生成的列,允许指定这一点,但仅指定从其生成表达式计算列的正常行为。

子选择

一种选择子查询产生的输出列与前面带括号的列列表中列出的一样多。子查询在执行时必须产生不超过一行。如果它产生一行,则将其列值分配给目标列;如果它没有产生任何行,则将 NULL 值分配给目标列。子查询可以引用正在更新的表的当前行的旧值。

from_item

允许其他表中的列出现在表中的表表达式在哪里条件和更新表达式。这使用与a的从句选择陈述;例如,可以指定表名的别名。不要将目标表重复为*from_item除非您打算自加入(在这种情况下,它必须在from_item*)。

(健康)状况

返回类型值的表达式布尔值.仅此表达式返回的行真的将会被更新。

游标名

在 a 中使用的游标名称当前位置健康)状况。要更新的行是最近从该游标中获取的行。游标必须是非分组查询更新的目标表。注意当前位置不能与布尔条件一起指定。看宣布有关使用游标的更多信息当前位置.

输出表达式

要计算并返回的表达式更新每行更新后的命令。表达式可以使用名为的表的任何列名*表名*或表中列出.写*返回所有列。

输出名称

用于返回列的名称。

# 输出

成功完成后,更新命令返回形式的命令标签

UPDATE count

这*数数是更新的行数,包括值未更改的匹配行。请注意,该数字可能小于匹配的行数(健康)状况当更新被更新前扳机。如果数数*为 0,查询没有更新任何行(这不被视为错误)。

如果更新命令包含一个返回子句,结果将类似于选择包含在返回列表,根据命令更新的行计算。

# 笔记

当一个子句存在,本质上发生的事情是目标表连接到表中提到的表*from_item*列表,连接的每个输出行代表目标表的更新操作。使用时您应该确保连接最多为要修改的每一行生成一个输出行。换句话说,目标行不应连接到来自其他表的多个行。如果是这样,那么只有一个连接行将用于更新目标行,但将使用哪一个是不容易预测的。

由于这种不确定性,仅在子选择中引用其他表更安全,但通常比使用连接更难阅读且速度较慢。

在分区表的情况下,更新行可能会导致它不再满足包含分区的分区约束。在这种情况下,如果分区树中有某个其他分区,该行满足其分区约束,则该行将移动到该分区。如果没有这样的分区,就会出错。在幕后,排运动实际上是一个删除插入手术。

有可能并发更新或者删除在被移动的行上会出现序列化失败错误。假设会话 1 正在执行更新在分区键上,同时该行可见的并发会话 2 执行更新或者删除对这一行进行操作。在这种情况下,会话 2更新要么删除将检测行移动并引发序列化失败错误(始终返回 SQLSTATE 代码“40001”)。如果发生这种情况,应用程序可能希望重试事务。在表未分区或没有行移动的通常情况下,会话 2 将识别新更新的行并执行更新/删除在这个新的行版本上。

请注意,虽然行可以从本地分区移动到外部表分区(假设外部数据包装器支持元组路由),但它们不能从外部表分区移动到另一个分区。

# 例子

换个词戏剧戏剧性在列种类表的电影

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

在表格的一行中调整温度条目并将降水重置为其默认值天气

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';

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

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

执行相同的操作,在在哪里条款:

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 salesmen
     WHERE salesmen.id = accounts.sales_id);

类似的结果可以通过连接来实现:

UPDATE accounts SET contact_first_name = first_name,
                    contact_last_name = last_name
  FROM salesmen WHERE salesmen.id = accounts.sales_id;

但是,第二个查询可能会产生意外的结果,如果推销员.id不是唯一键,而如果有多个查询,则保证第一个查询会引发错误id火柴。此外,如果没有特定的匹配项帐户.sales_id条目,第一个查询会将相应的名称字段设置为 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目前定位:

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

# 兼容性

此命令符合 SQL 标准,除了返回子句是 PostgreSQL 扩展,使用的能力也是如此更新.

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

根据标准,带括号的目标列名称子列表的源值可以是产生正确列数的任何行值表达式。PostgreSQL 只允许源值是行构造函数或子选择.单个列的更新值可以指定为默认在行构造函数的情况下,但不在子内部选择.