UPDATE 修改已有的行
INSERT 添加新行,UPDATE 修改已经存在的行。它的形式很短,值得记住:
UPDATE table_name
SET column = value
WHERE condition;
一个可以运行的例子:
SET 说明改什么,WHERE 说明改哪些行。表中其余的部分保持不变。
实际上 WHERE 子句不能省
从技术上讲,WHERE 是可选的。但在实际中,省略它正是新手工程师毁掉一个下午的方式:
UPDATE users SET status = 'inactive';
-- every single user is now inactive
没有过滤条件,就意味着每一行都匹配。SQLite 会默默照办。永远先写 WHERE,再写 SET,光是这个习惯就能避免很多事故。
如果不确定 WHERE 写得对不对,先用同样的条件执行一次 SELECT:
同一个条件,两条语句。SELECT 就是你的预演。
更新多列
在同一个 SET 里用逗号分隔各个赋值。一个 SET,多个列:
访问一次数据库,修改一行,更新三列。一条语句能做完的事,不要拆成三条 UPDATE。
= 右边可以是表达式
= 后面的值不一定是字面量。它可以是任何表达式,包括引用这一列当前值的表达式:
price * 1.10 读取现有的价格,乘以系数,再把结果写回去。SQLite 计算右边时,用的是这条语句的任何赋值生效之前的行数据,所以你可以放心地引用多个列:
UPDATE products SET price = price * 1.10, stock = stock + price;
-- 'price' on the right side here is the OLD price, not the just-updated one.
UPDATE ... FROM:从另一张表取值
从 SQLite 3.33 开始,UPDATE 支持 FROM 子句,可以做跨表更新。这是在表之间同步数据最简洁的方式:
子查询算出每个客户的总额,外层的 UPDATE 再按 id 把这些结果连接回 customers。如果没有 UPDATE ... FROM,你就得为每一列写一个相关子查询,冗长得多。
有几条规则要记住:
- 目标表写在
UPDATE后面,而不是放在FROM列表里。 - 连接由
WHERE子句完成,这里没有ON关键字。 - 如果连接可能在
FROM中匹配到不止一行,结果是不确定的。确保你的连接键对每个目标行最多只产生一个匹配。
RETURNING:查看改了什么
SQLite(3.35 及以上)允许 UPDATE 在同一条语句中返回被修改的行。当应用需要更新后的值,又不想再执行一次 SELECT 时,这很方便:
你拿到的是真正被修改的那些行,以及它们的新值。这样既省了一次往返,也消除了并发代码中的一类竞态条件。本章后面有专门介绍 RETURNING 的一页。
UPDATE OR REPLACE:处理约束冲突
如果更新会违反 UNIQUE 约束,默认行为是报错并中止语句。OR 子句让你选择不同的处理策略:
可选项有 OR ABORT(默认)、OR REPLACE、OR IGNORE、OR FAIL 和 OR ROLLBACK。REPLACE 是危险的那个:它会删除冲突的行,而这可能通过外键产生级联影响。只有当你真的想表达“如果已经有一行用了这个唯一值,就把它扔掉”时才用它。
对于大多数 upsert 类的操作,专门的 INSERT ... ON CONFLICT 语法更清楚。有一页专门讲它。
把有风险的更新放进事务
当你要修改大量的行,或者执行多条必须一起成功的 UPDATE 语句时,把它们放进一个事务。万一出了问题,你可以回滚到之前的状态:
如果第二条语句失败(比如触发了某个约束),ROLLBACK 会撤销第一条。没有事务的话,你会留下一笔只完成了一半的转账:Ada 少了 25,Boris 却没变。事务在后面有专门的一章;现在只要知道有它,而且批量更新几乎总是应该放在事务里。
常见陷阱
几个容易踩的坑:
- 忘了写
WHERE:会更新每一行。执行之前把语句大声读一遍。 WHERE里用错运算符:WHERE status = NULL什么都匹配不到,要用IS NULL。运算符那一页会讲到。- 用返回多行的子查询来更新,而你预期的是一行。用
LIMIT 1或者对子查询做聚合,否则会报错或得到意外的结果。 - 把 UPDATE OR REPLACE 和 UPSERT 混为一谈。
OR REPLACE会删除冲突的行,INSERT ... ON CONFLICT DO UPDATE则是就地修改它们。两者是不同的操作。
下一步:DELETE
UPDATE 修改行,DELETE 删除行。同样要严格对待 WHERE,同样的“先执行 SELECT”习惯,也能让你免于同类的灾难。这就是下一页的内容。
常见问题
SQLite 中 UPDATE 的基本语法是什么?
UPDATE table_name SET column = value WHERE condition;。SET 子句列出要修改的列和它们的新值。WHERE 子句决定修改哪些行,省略它的话,表中的每一行都会被更新。
如何在一条语句中更新多列?
在 SET 子句中用逗号分隔各个赋值:UPDATE users SET name = 'Ada', email = 'ada@x.com' WHERE id = 1;。一条语句,访问一次数据库,更新一行。不要为每一列重复写 SET。
SQLite 能用另一张表的数据更新表吗?
能。UPDATE ... FROM(SQLite 3.33 加入)允许你在更新时连接另一张表或子查询。写法是 UPDATE target SET col = source.col FROM source WHERE target.id = source.id;。这是在表之间复制值最简洁的方式。