update 表 set 列 = 值 where 条件——set 说改什么,where 说改哪些行。
最好用的一招是「用原值算新值」:SET stock = stock + 10,
每行各自加 10,200 变 210、0 变 10。 但并发时它会丢更新——
两个人各加 10 可能只加到 210。
忘写 where 就是全表(Demo 里影响 3 行)。
两条保命习惯:先用 select 验 where 的范围,再用 where 1 = 0 空跑。
上一篇是「加」,这一篇是「改」——三类语句里最容易出事的一种。
insert 错了顶多多几行脏数据,update 错了会把原来的值覆盖掉。 而覆盖是看不出痕迹的
:你不知道它原本是什么。
一、基本写法:set 与 where 各管一半
set 说改哪一列、改成什么;where 说改哪些行。 两个都写清楚,改动才可控。
多列用逗号连在同一个 set 后面:SET room = 'A101', teacher = '马老师'。
拆成两条 update 也能改,但要扫两遍表,还可能中途失败只改了一半。
set 里不能用聚合函数,也不能引用别的表(除非用子查询)。
要「按另一张表的值来改」,得写子查询或 join。
二、用原值算新值:最好用的一招
SET stock = stock + 10 里的 stock 指的是「这一行原来的值」。 所以每行各自加 10——
跳绳 200 变 210、运动水壶 0 变 10。
这一招做库存扣减、计数累加、分数调整时天天用。 关键是「不要先查出来再算好写回去」,
那样多一次往返,还会碰上并发问题。
但并发时它会丢更新:两个请求同时读到 200,各自加 10 写回 210,
结果只加了一次。 这叫「丢失更新」。 解法是加锁或者用事务(第 18 章),
或者干脆依赖数据库自己的行锁——这也是为什么「用原值算」比「查出来算」安全。
三、忘写 where:不是错误,是范围错了
UPDATE classes SET teacher = '全部' 不写 where,影响 3 行,全改了。
它不报错,因为语法完全正确。
这是最阴的一类事故:语句成功、影响行数还挺「合理」,
数据却已经被覆盖,而且原来的值再也查不回来。
唯一的保险是习惯:update 打完,先回头看一眼有没有 where,
再看一眼条件对不对。 两个动作各两秒钟,能挡掉大半事故。
四、两条保命习惯:先查、再空跑
习惯一:把 where 先拿去做 select。
SELECT id, name FROM classes WHERE id > 2 看到一行「三班」,
确认这就是你想改的范围,再把同一段条件粘过去。
习惯二:where 1 = 0 空跑。 影响 0 行、什么都没改,但语句跑通了——列名写错、
类型不匹配这类问题会在这时候暴露。
MySQL 还有个 safe-updates 模式:
不写 where、或者 where 里没用主键/索引的 update 直接拒绝。 生产环境建议开,
它是最后一道防线。 本沙箱没有这个模式。
五、update 配 order by 与 limit:MySQL 有,这里没有
MySQL 支持 UPDATE … ORDER BY 列 LIMIT n——比如「只改最早的三条」。
这是 MySQL 的扩展,标准 SQL 里没有。
本沙箱不支持,limit 只能用在 select 上。
要在这边实现同样的效果,得先用子查询查出主键,再按主键改。
这个差异要留意:教材里出现 update … limit 的地方,在这边一律跑不了,得改写成两步。
同理 delete … limit 也一样。
六、动手 Demo:改一行、改多列、加库存,最后撞一次「忘了 where」
Demo 六段:基本写法、多列更新、用原值算新值、忘写 where、两条保命习惯、以及 limit 那个方言。
第四段是这一篇的重点——它会真的把三行全改掉,让你看清「没有报错」比「报错」危险得多。
import _q46 as Q
c = Q.con()
# 基本写法
Q.run(c, "UPDATE classes SET teacher = '孔老师(代)' WHERE id = 2")
# 一次改多列
Q.run(c, "UPDATE classes SET room = 'A101', teacher = '马老师' "
"WHERE id = 1")
# 用原值算新值
Q.run(c, "UPDATE goods SET stock = stock + 10 WHERE cat = '配件'")
# 忘写 where:影响 3 行
c2 = Q.con()
Q.run(c2, "UPDATE classes SET teacher = '全部'")
# 空跑一次
Q.run(c2, "UPDATE classes SET teacher = '试运行' WHERE 1 = 0")
# 先 select 验证 where 范围
c2.execute("SELECT id, name FROM classes WHERE id > 2")
实跑输出:
--------------------------------------------------------------------
第三十三课:update,以及「忘了写 where」这一课
--------------------------------------------------------------------
基本写法:set 改哪列,where 定范围
SQL> SELECT id, name, teacher, room FROM classes WHERE id = 2
id name teacher room
------------------------------------
2 二班 孔老师 A102
(1 行)
SQL> UPDATE classes SET teacher = '孔老师(代)' WHERE id = 2
影响 1 行
SQL> SELECT id, name, teacher, room FROM classes WHERE id = 2
id name teacher room
-----------------------------------------
2 二班 孔老师(代) A102
(1 行)
**`update 表 set 列 = 值 where 条件`**——**`set` 说改什么,`where` 说改哪些行。** 两条都清楚,
改动才可控。
一次改多列:逗号隔开
SQL> UPDATE classes SET room = 'A101', teacher = '马老师' WHERE id = 1
影响 1 行
**多列用逗号连起来写在同一个 `set` 后面**,只跑一次。
**拆成两条 `update` 也能改,但那要扫两遍表,还可能中途失败只改了一半。**
用原值算新值:这是 update 最好用的一招
SQL> SELECT name, stock FROM goods WHERE cat = '配件' ORDER BY id
name stock
-------------------
跳绳 200
护腕 300
运动水壶 0
瑜伽垫 60
(4 行)
SQL> UPDATE goods SET stock = stock + 10 WHERE cat = '配件'
影响 4 行
SQL> SELECT name, stock FROM goods WHERE cat = '配件' ORDER BY id
name stock
-------------------
跳绳 210
护腕 310
运动水壶 10
瑜伽垫 70
(4 行)
**`stock = stock + 10` 里的 `stock` 指的是「这一行原来的值」**,所以每行各自加 10——200 变 210、
0 变 10。
**这一招做库存扣减、计数累加时天天用。** **但并发时要注意**:两个请求同时读到 200,各加 10,
**最后可能只加到 210 而不是 220**,这叫丢失更新。 **第 18 章讲锁和事务时会回来看这个问题。**
忘写 where 的后果
SQL> SELECT COUNT(*) AS before_rows FROM classes WHERE teacher <> '全部'
before_rows
------------
3
(1 行)
SQL> UPDATE classes SET teacher = '全部'
影响 3 行
SQL> SELECT COUNT(*) AS changed FROM classes WHERE teacher = '全部'
changed
--------
3
(1 行)
**影响 3 行,全班老师都被改成了「全部」。**
**这就是没写 `where` 的结果——它不是错误,是「范围写成了整张表」。**
**唯一的保险是习惯**:`update` 和 `delete` 打完,
**先回头看一眼有没有 `where`,再看一眼条件对不对。**
两条安全习惯:空跑与先查
**习惯一,先把 `where` 拿去做 `select`。** 想知道「会改到哪几行」,
先跑 `SELECT * FROM classes WHERE 条件`。 人对了,再把同一段 `where` 粘到 `update` 上。
SQL> SELECT id, name FROM classes WHERE id > 2
id name
------------
3 三班
(1 行)
**习惯二,用 `where 1 = 0` 空跑一次。** 影响 0 行、什么都没改,但语句本身跑通了,
**能提前发现列名写错、类型不匹配这类问题。**
SQL> UPDATE classes SET teacher = '试运行' WHERE 1 = 0
影响 0 行
update 配 order by 与 limit:MySQL 有,这里没有
**MySQL 支持 `UPDATE ... ORDER BY 列 LIMIT n`**——比如「只改最早的三条」。
**这是 MySQL 的扩展,标准 SQL 里没有。**
**本沙箱不支持**,`limit` 只能用在 `select` 上。
**要在这边实现同样的效果,得先用子查询查出主键,再按主键改。**
**MySQL 还有个 `safe-updates` 模式**(客户端加 `--safe-updates` 开启):
**不写 `where`、或者 `where` 里没用主键/索引的 `update` 直接拒绝执行。** **生产环境建议开**,
它能挡住最蠢的那种事故。
第一段改一行。 二班老师加「(代)」,影响 1 行。
第二段改多列。 房间号和老师一起改,只跑一遍。
第三段加库存。 四件配件各加 10:200→210、300→310、0→10、60→70。
注意 0 变 10 那一行——0 + 10 也是 10,不是「0 加了还是 0」。
第四段忘写 where。 影响 3 行,全班老师变「全部」。
第五段两条习惯。 先 select 看到「三班」一行;再 where 1 = 0 空跑,影响 0 行。
第六段没有 SQL,讲的是 limit 那个方言和 safe-updates。
30 秒实验
UPDATE goods SET stock = stock * 2 WHERE stock > 0会影响几行?数值变成多少?UPDATE classes SET teacher = 'x' WHERE id = 99影响几行?什么意思?- 要把「库存为 0 的那行的
on_sale改成 0」,怎么写?先空跑一遍。
常见误区
- 不写
where就回车。 语法完全正确,所以不报错,但整张表都被覆盖,原来的值再也查不回来。 - 先
select出来在程序里算好,再update写回去。 多一次往返,并发时还会丢更新。 直接用SET 列 = 列 + 值。 - 以为「影响 0 行」是执行失败。 是条件没匹配上,该改条件,不是改语法。
自测三问(点开对答案)
1. `SET stock = stock + 10` 里的 `stock` 指什么? —— 指「这一行原来的值」,所以每行各自加 10,而不是统一变成某个值。 2. 为什么「忘了写 where」比「写错语法」更危险? —— 写错语法会立刻报错、什么都没改;忘写 `where` 语法正确、照样执行,整张表被覆盖且无法撤销。 3. `update ... limit` 在本沙箱能用吗? —— 不能,那是 MySQL 的扩展。要用得先查出主键再按主键改。改完了,还剩「删」。下一篇 delete,以及两种删不掉、删不干净的情况。
- ☐ (本篇完,下一篇 7.4 删除数据:delete)