UPDATE:更新数据
本教程共 50 篇 · 第 21 篇 · 更新于 2026-07-31
21. UPDATE:更新数据
本节目标:学完本章你能用 UPDATE 修改一行或多行数据,会在 SET 里写表达式计算新值,并养成”更新前先 SELECT 校验”的习惯,绝不漏写 WHERE。
数据是会变的:用户改了昵称、商品降价了、订单状态从”待付款”变”已发货”。这些”改已有记录”的动作,靠的就是 UPDATE 语句。它和 INSERT(新增)、DELETE(删除)并列为最基础的三类写操作。
UPDATE 的最基本形态:
UPDATE users
SET email = 'new@example.com'
WHERE id = 2;
语义是:对 users 表,把满足 WHERE 条件的那些行,按 SET 后面的”列 = 新值”逐个改掉。这里只改了 id = 2 那一行的 email。可以一次改多列,用逗号隔开:
UPDATE users
SET name = 'Robert', age = 31
WHERE id = 2;
SET 右边不限于写死的常量,完全可以是一个表达式,引用本行的其它列。最常见的例子是”在原值基础上加减”:
UPDATE users
SET age = age + 1
WHERE id = 3;
这行把 id = 3 的用户年龄加 1。注意 age = age + 1 右边的 age 指的是”这行改之前的 age”,SQLite 会先算出表达式的值,再写回该列。你也可以用函数,比如将姓名统一转大写:SET name = upper(name)。
说到 id,我们的 users 表主键是 INTEGER PRIMARY KEY——这正是 SQLite 隐式自增的写法:插入时不指定 id,SQLite 会自动分配一个比当前最大 ROWID 更大的整数(它是 ROWID 的别名)。正因如此,你通常不需要、也不该用 AUTOINCREMENT 关键字(那只会额外引入 sqlite_sequence 表的维护开销,且仅在”永不复用已删 id”这种罕见需求下才有意义)。日常更新时,除非确有必要,别去改写主键 id,否则可能破坏与其它表的外键关联。
UPDATE 真正危险的地方只有一个字:漏写 WHERE。WHERE 在 UPDATE 里是”可选的”,但一旦你省掉它,SQLite 会认为”你想改全表”,于是对每一行执行 SET。下面这句:
UPDATE users SET email = 'x@x.com';
没有 WHERE,于是表里所有人的邮箱瞬间全变成 x@x.com——而且默认情况下这波修改很难撤回。所以业界铁律是:写 UPDATE 前,先用一模一样的 WHERE 跑一次 SELECT,确认命中的行正是你想改的那些,再把 SELECT 改成 UPDATE 执行。
-- 先预览会命中哪些行
SELECT * FROM users WHERE age < 18;
-- 确认无误后再更新
UPDATE users SET age = 18 WHERE age < 18;
顺带说一句外键。如果别的表(比如 orders 的 user_id)引用了 users 的 id,而你改了某个被引用的 id,SQLite 默认并不会自动去维护子表里对应的外键值——因为外键约束在 SQLite 里默认是关闭的。想让 SQLite 帮你强制参照完整性(比如拒绝修改、或级联更新),必须先执行 PRAGMA foreign_keys = ON,而且这个开关每次新建连接都要重新打开。
重点提示
SET 子句里可以写表达式,右边能引用同一行的其它列(如 age = age + 1、name = upper(name)),这让”基于旧值算新值”变得非常自然。
实用技巧
更新前先用 SELECT + 相同 WHERE 预览命中行,是避免误改的最便宜保险。把”先 SELECT 后 UPDATE”当成肌肉记忆,能救你无数次。
常见坑
漏写 WHERE 的 UPDATE 会改掉整张表!执行前务必确认条件。另外,若本表被其它表外键引用,想让 SQLite 维护参照完整性必须 PRAGMA foreign_keys = ON(默认关闭,且每次连接都要开);否则改了父表主键,子表会留下对不上号的”孤儿”数据。
SQLite 3.35.0 起(3.53.4 基线当然包含)支持 UPDATE … RETURNING:更新后直接把被改的行返回给你,省掉再发一条 SELECT 去确认的麻烦。例如:
UPDATE users SET age = age + 1 WHERE id = 3 RETURNING name, age;
一行就能”改完并看到结果”。
再做个对比帮助理解:UPDATE 是”改这一行里指定的列”,没提到的列原样保留;这和”先 DELETE 再 INSERT 一条新行”完全不同——后者会换掉整行的 ROWID、触发外键级联、丢掉未提及的列,代价大得多。所以”只改几个字段”永远用 UPDATE,不要想着重建整行。
最后补一句安全细则:UPDATE 同样可以包进事务(BEGIN … COMMIT / ROLLBACK)。当你要一口气改很多行、或改的是关键业务数据,先 BEGIN,改完 SELECT 抽查,确认无误再 COMMIT,发现不对就 ROLLBACK。把”事务护体”和”先 SELECT 预览”叠加使用,是生产环境改数据的双保险。
适用场景再强调:资料编辑、价格调整、状态机流转(待付款→已付款→已发货)、计数器累加(阅读量 +1)。每当你想”改已有数据”,先问自己三件事:改哪张表?WHERE 精确命中哪些行?SET 的表达式对不对?三问过关,再动手。
最后点一个新手高频笔误:SET 后面要给多个列赋值时,列与列之间用逗号分隔,而不是 AND。写成 UPDATE users SET age = age + 1 AND name = ‘Tom’ WHERE … 在语法上”合法”,但 AND 在这里会被当成一个布尔表达式,整列被赋成 0 或 1,和你想要的”两个字段分别更新”完全两回事。记住:多列用逗号,条件才用 AND(且只在 WHERE 里)。
还有,SET 的值如果是字符串,务必用单引号包起来;漏了引号 SQLite 会把那串文字当成列名去解析,轻则报”找不到该列”,重则更新错列、把数字写进文本列。字符串用单引号、数字直接写,这是 SQL 的基础纪律,和 INSERT 时一模一样。
UPDATE 执行后,如果不放心,可以马上用 SELECT 看一下改后的样子——这正是前面”先 SELECT 预览”习惯的延续:预览用 SELECT,确认用 SELECT,只有真正动手那一步才换成 UPDATE。把查询当显微镜、更新当手术刀,先看清再下刀,数据就安全。这套节奏练熟后,你几乎不会再误改数据,也少了许多半夜爬起来救火的焦虑。
类比小结:UPDATE 像给通讯录里的联系人贴改正贴——WHERE 就是”我要找的这个人”的精确描述,SET 是”把哪条信息撕掉换成新的”。要是描述写得太宽泛(或干脆没写),改正贴就会贴到所有人身上。所以动笔前,先在通讯录里搜一下”会搜到谁”,是基本礼貌也是基本安全。
什么时候用 UPDATE?任何”数据发生变化但记录还在”的场景:改资料、调价格、切换状态、累计计数。只要记住”条件要准、先查后改”,UPDATE 就是最可靠的工具。