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) } [, ...] [ FROMfrom_item[, ...] ] [ WHEREcondition| WHERE CURRENT OFcursor_name] [ RETURNING * |output_expression[ [ AS ]output_name] [, ...] ]
描述
UPDATE 会更改所有满足条件的行中指定列的值。只需在 SET 子句中提及需要修改的列;未被显式修改的列会保留其原有值。
有两种方法可以利用数据库中其他表所包含的信息来修改一个表:使用子选择,或者在 FROM 子句中指定附加表。哪种技术更合适取决于具体情况。
可选的 RETURNING 子句使 UPDATE
基于每个实际更新的行计算并返回一个或多个值。可以计算任何使用该表列和/或
FROM 中提到的其他表列的表达式。使用该表列的新值(更新后值)。RETURNING 列表的语法与 SELECT
的输出列表相同。
必须拥有该表上的 UPDATE 权限,或者至少拥有待更新列的 UPDATE 权限。对于其值会在
expressions 或者
condition 中读取的任何列,还必须拥有 SELECT 权限。
参数
with_queryWITH子句允许你指定一个或多个子查询,这些子查询可在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条件和更新表达式中。这里使用的语法与FROM子句相同,后者属于SELECT语句;例如,可以为表名指定别名。不要把目标表重复写成from_item,除非你打算进行自连接(这种情况下它必须在from_item中带有别名出现)。condition一个返回
boolean类型值的表达式。只有使这个表达式返回true的行才会被更新。cursor_name要在
WHERE CURRENT OF条件中使用的游标名。要被更新的行是最近一次从该游标中取出的那一行。该游标必须是针对UPDATE目标表的非分组查询。注意,WHERE CURRENT OF不能与布尔条件同时指定。有关将游标用于WHERE CURRENT OF的更多信息,请见 DECLARE。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 时,应确保对每一个要修改的目标行,连接至多生成一条输出行。换言之,一条目标行不应与其他表中的多于一行成功连接。如果发生这种情况,则只会使用其中某一条连接行来更新目标行,但具体使用哪一条并不容易预测。
因为存在这种不确定性,所以仅在子选择中引用其他表会更安全,尽管这种写法通常比使用连接更难阅读、也更慢。
对于分区表,更新一行可能会导致它不再满足分区约束。由于没有机制将该行移动到与其分区键新值相适应的分区中,这种情况会引发错误。直接更新某个分区时,也可能发生这种情况。
示例
把表 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 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;但是,第二个查询可能会给出意外结果,如果 salesmen.id 不是唯一键的话;而第一个查询在存在多个 id 匹配时保证会报错。此外,如果某个特定的 accounts.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;
-- 其他操作
SAVEPOINT sp1;
INSERT INTO wines VALUES('Chateau Lafite 2003', '24');
-- 假设上面的语句因唯一键冲突而失败,
-- 那么现在发出这些命令:
ROLLBACK TO sp1;
UPDATE wines SET stock = stock + 24 WHERE winename = 'Chateau Lafite 2003';
-- 继续执行其他操作,最后
COMMIT;
更改表 films 中游标 c_films
当前定位那一行的 kind 列:
UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films;
兼容性
这个命令符合 SQL 标准,不过
FROM 和 RETURNING 子句是
PostgreSQL 扩展,把 WITH
与 UPDATE 一起使用的能力也是扩展。
某些其他数据库系统提供一种 FROM 选项,要求在
FROM 中再次列出目标表。PostgreSQL
并不是这样解释 FROM 的。在移植使用这种扩展的应用时要小心。
根据标准,目标列名的一个圆括号子列表的源值可以是任何能够产生正确列数的行值表达式。PostgreSQL 只允许该源值是一个行构造器或子 SELECT。在行构造器的情况下,单个列的更新值可以指定为 DEFAULT,但在子 SELECT
中则不能这样做。