首页 / PostgreSQL 入门教程 / UPDATE 更新数据

PostgreSQL 入门教程

UPDATE 更新数据

本教程共 50 篇 · 第 19 篇 · 更新于 2026-07-31 · 约 7 分钟阅读

PostgreSQLPostgreSQL 入门教程UPDATESETWHERERETURNING

19. UPDATE 更新数据

本节目标:学完你能用 UPDATE 修改表里已有的数据,知道必须加 WHERE 来限定范围,也知道怎么顺手取回被改动的行,还会用子查询、跨表更新和 CASE 分支。

更新(UPDATE)用来修改已经存在的行。它和 INSERT 不同:INSERT 是「加新数据」,UPDATE 是「改老数据」。

基本语法

UPDATE 表名
SET 列1 = 新值1,
    列2 = 新值2
WHERE 条件;

SET 后面写要改的列和值,可以一次改多列。WHERE 决定改哪些行。

先往 users 里放几条数据方便演示:

INSERT INTO users (username, email, age)
VALUES
  ('xiaoming', 'ming@example.com', 28),
  ('xiaohong', 'hong@example.com', 22),
  ('wangwu',   'wang@example.com', 35);

再给 orders 放点数据,后面跨表更新用得上:

INSERT INTO orders (user_id, amount, status)
VALUES
  (1, 99.90,  'pending'),
  (2, 199.00, 'pending'),
  (3, 59.00,  'paid');

在 psql 里执行更新后,提示符会回一句受影响的行数:

postgres=# UPDATE users SET age = 29 WHERE username = 'xiaoming';
UPDATE 1

改符合条件的某一行

xiaoming 的年龄改成 29:

UPDATE users
SET age = 29
WHERE username = 'xiaoming';

回显是 UPDATE 1,表示改动了 1 行。

一次改多列

SET 后面用逗号隔开,可以一次改好几列。比如同时改邮箱和年龄:

UPDATE users
SET email = 'hong2@example.com',
    age = 23
WHERE username = 'xiaohong';
Note

SET 后面多列之间用逗号分隔,不是 AND。写成 SET age = 30 AND email = 'x' 不会被当成「两列」,而是会把整段当成一个布尔表达式去算,结果会写错列。这是新手常见的笔误。

用表达式计算新值

SET 右边不一定是固定值,也可以是一个表达式。比如给所有用户年龄加 1 岁:

UPDATE users
SET age = age + 1;
Warning

上面这条没有 WHERE,于是表里每一行的 age 都会加 1。UPDATE 一旦漏写 WHERE,影响范围是整张表,后果很难挽回。我之前就因为少打一个 WHERE,把整张配置表的状态全刷掉了,所以这个坑一定要记牢。

用 RETURNING 取回改后的行

和 INSERT 一样,UPDATE 也能用 RETURNING 把改动后的行返回来,方便立刻确认结果。

UPDATE users
SET age = 30
WHERE username = 'wangwu'
RETURNING id, username, age;

输出类似:

 id | username | age
----+----------+-----
  3 | wangwu   |  30
(1 row)

WHERE 决定改哪些行

WHERE 里的条件可以很灵活。WHERE id = 2 只改一行;WHERE age < 25 会改所有年龄小于 25 的行。

UPDATE users
SET age = age + 10
WHERE age < 25;
Tip

不确定 UPDATE 会改多少行时,先用同条件的 SELECT 查一下:

SELECT * FROM users WHERE age < 25;

确认范围对了,再把 SELECT * 改成 UPDATE ... SET 执行。这是个稳妥的好习惯。

SET 右边用子查询

SET 的新值还能来自一条子查询(subquery,嵌套在里面的查询)。比如把 xiaoming 的年龄改成和 wangwu 一样:

UPDATE users
SET age = (SELECT age FROM users WHERE username = 'wangwu')
WHERE username = 'xiaoming';

子查询必须只返回「一行一列」,否则会报错。它适合「从一个数据派生出另一个数据」的场景。

用 FROM 跨表更新

UPDATE 还能借助 FROM 关联另一张表来算新值。比如把所有年龄大于 30 岁的用户,其订单状态标记为 'vip'

UPDATE orders
SET status = 'vip'
FROM users
WHERE orders.user_id = users.id
  AND users.age > 30;

这里 FROM users 把两张表按 user_id = id 连起来,只有匹配上的订单才会被改。跨表更新在数据同步、批量打标时很常用。

Note

跨表更新时,要在 WHERE 里写清两表的连接条件(如 orders.user_id = users.id)。漏了连接条件会产生「笛卡尔积」,把目标表每行都和另一表每行配对,结果往往改错一大片。

把列改成 NULL 或当前时间

更新也能把某列清空成 NULL,或者用函数填当前时间:

-- 清空邮箱
UPDATE users
SET email = NULL
WHERE username = 'xiaohong';

-- 把注册时间刷新为现在
UPDATE users
SET created_at = now()
WHERE username = 'xiaoming';

用 CASE 做条件更新

SET 右边还能写 CASE 表达式,按不同情况赋不同值。比如按金额给订单打标签:

UPDATE orders
SET status = CASE
  WHEN amount >= 100 THEN 'big'
  WHEN amount >= 10  THEN 'normal'
  ELSE 'small'
END;

这条会逐行判断:金额够 100 标 big,够 10 标 normal,其余标 small。一次 UPDATE 就完成分档,比写好几条 UPDATE 清爽。

Note

CASE 表达式的完整写法留到后面的「条件表达式」章节细讲,这里先知道「SET 里可以写更复杂的分支逻辑」即可。

批量调价与改文本

SET 右边可以是任意算术,比如给所有待支付订单打 9 折:

UPDATE orders
SET amount = amount * 0.9
WHERE status = 'pending';

文本列也能在更新里拼接。比如给所有「还没带 @」的邮箱补上统一后缀:

UPDATE users
SET email = email || '@example.com'
WHERE email NOT LIKE '%@%';

|| 是 PostgreSQL 的字符串拼接符,LIKE 用来判断「还没带 @」。

RETURNING 也能返回多行

当 UPDATE 命中多行时,RETURNING 会把每一行被改后的结果都列出来:

UPDATE orders
SET amount = amount * 0.9,
    status = 'discounted'
WHERE status = 'pending'
  AND amount > 100
RETURNING id, amount, status;

配合 RETURNING,你不用再发一条 SELECT 去确认改了谁,一条语句全搞定。

UPDATE 与事务

UPDATE 本身是一条语句,要么全成功要么出错回滚。如果你想「先改,看看结果,不对再撤销」,可以把多条语句包在事务里:

BEGIN;
UPDATE users SET age = 99 WHERE username = 'xiaoming';
-- 反悔了,撤销
ROLLBACK;

事务的详细玩法后面章节再展开,这里只要记住:怕改错时,可以先 BEGIN 再操作,确认无误 COMMIT,不对就 ROLLBACK

常见错误与现象

  • 漏写 WHERE:改动整张表,且无法自动撤销(除非在事务里回滚)。执行前务必确认。
  • WHERE 没匹配到任何行:返回 UPDATE 0,这不是报错,只是没动到数据。先确认条件写对。
  • SET 多列用 AND 连接:会把整段当成布尔表达式,写进第一个列,类型不对时报错。

WHERE 不止用于 SELECT

前面几章提过,UPDATE 和 DELETE 里的 WHERE 逻辑完全一样。随手两个例子复习一下:

-- 只改资深用户的订单状态
UPDATE orders
SET status = 'vip'
WHERE user_id IN (SELECT id FROM users WHERE age > 30);

-- 只删没有邮箱的用户
DELETE FROM users WHERE email IS NULL;

一个 WHERE,决定了「动哪些行」。不管是查、改还是删,套路都一样。

动手前先想清范围

每条 UPDATE 执行前,我习惯在心里过一遍三件事:

  1. 我要改的是哪张表?表名别写错。
  2. WHERE 条件到底圈住了哪些行?拿不准就先 SELECT 看一眼。
  3. SET 的值对不对?尤其是表达式和子查询,先算一遍。

这三步花不了一分钟,却能避开绝大多数「改错行」的事故。我踩过的坑,基本都是跳了其中某一步。

Tip

改完用 RETURNING 看结果,是确认「改对行」成本最低的办法。比起再发一条 SELECT 核对,它就在同一条语句里完成,不会漏看中间状态。

小结

  • UPDATE 用 SET 列=值 改数据,WHERE 锁定要改的行。
  • 省略 WHERE 会改动整张表,执行前务必确认条件。
  • SET 右边可以是表达式、子查询,甚至 CASE 分支。
  • FROM 能让 UPDATE 借助另一张表算出新值。
  • RETURNING 能直接返回被改动的行;怕改错可包在事务里 ROLLBACK