UPDATE 更新数据
本教程共 50 篇 · 第 19 篇 · 更新于 2026-07-31 · 约 7 分钟阅读
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';
NoteSET 后面多列之间用逗号分隔,不是
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 清爽。
NoteCASE 表达式的完整写法留到后面的「条件表达式」章节细讲,这里先知道「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 执行前,我习惯在心里过一遍三件事:
- 我要改的是哪张表?表名别写错。
- WHERE 条件到底圈住了哪些行?拿不准就先 SELECT 看一眼。
- SET 的值对不对?尤其是表达式和子查询,先算一遍。
这三步花不了一分钟,却能避开绝大多数「改错行」的事故。我踩过的坑,基本都是跳了其中某一步。
Tip改完用
RETURNING看结果,是确认「改对行」成本最低的办法。比起再发一条 SELECT 核对,它就在同一条语句里完成,不会漏看中间状态。
小结
- UPDATE 用
SET 列=值改数据,WHERE锁定要改的行。 - 省略 WHERE 会改动整张表,执行前务必确认条件。
- SET 右边可以是表达式、子查询,甚至 CASE 分支。
FROM能让 UPDATE 借助另一张表算出新值。RETURNING能直接返回被改动的行;怕改错可包在事务里ROLLBACK。