7、数据更新与删除:UPDATE语句、DELETE语句、条件更新与删除、事务保护

好,咱们今天聊点实在的。数据库里光能查数据、插数据,那只是基本功。真正干活的时候,改数据和删数据才是家常便饭。你想想看,一个订单状态从「待支付」变成「已支付」,这不就是UPDATE吗?用户要注销账号,这不就是DELETE吗?

但这里头坑不少。我见过太多新手,一条DELETE下去,整张表清空了,然后对着屏幕发呆。嗯,咱们今天就把这些操作掰开揉碎了讲清楚。

7.1 UPDATE语句:改数据的基本功

UPDATE的语法其实很简单,核心就三部分:你要改哪张表、你要改哪些字段、你要改哪些行。

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

我在项目中遇到过最典型的场景:用户修改个人信息。比如改手机号:

UPDATE users 
SET phone = '13800138000', update_time = datetime('now')
WHERE user_id = 1001;

这里有个细节我要强调一下——SET子句里可以写表达式。比如给所有商品涨价10%:

UPDATE products 
SET price = price * 1.1
WHERE category = '电子产品';

你看,price = price * 1.1,这种写法在SQL里完全合法。我第一次看到时还愣了一下,心想这也能行?后来发现,这其实是UPDATE最常用的技巧之一。

小提示:UPDATE时如果省略WHERE子句,会更新整张表的所有行。这通常不是你想要的。写UPDATE之前,先确认WHERE条件对不对,这是个好习惯。

7.2 DELETE语句:删数据要三思

DELETE的语法比UPDATE还简单:

DELETE FROM 表名 WHERE 条件;

但越简单的东西,越容易出事。我曾经有个同事,在测试环境执行了一条:

DELETE FROM orders;

然后整个测试库的订单数据全没了。他当时脸都绿了。还好是测试环境,有备份。但如果是生产环境呢?

所以我的习惯是:写DELETE之前,先用SELECT查一遍。比如你想删掉所有一年未登录的僵尸用户:

-- 先查,确认是哪些数据
SELECT user_id, username, last_login 
FROM users 
WHERE last_login < date('now', '-1 year');

-- 确认无误后,再删
DELETE FROM users 
WHERE last_login < date('now', '-1 year');

说白了,这就是「先看后删」原则。你多花10秒钟确认,可能就省了后面10个小时的恢复工作。

警告:DELETE操作不可逆(除非有事务保护)。生产环境执行DELETE前,务必确认备份或事务机制是否就绪。

7.3 条件更新与删除:精确打击

实际业务中,很少有无条件更新或删除的场景。条件写得越精确,出错的概率越低。

常见的条件写法包括:

  • 等值条件:WHERE status = 'inactive'
  • 范围条件:WHERE price BETWEEN 100 AND 500
  • 模糊匹配:WHERE email LIKE '%@spam.com'
  • 复合条件:WHERE age > 60 AND level < 5
  • 子查询条件:WHERE user_id IN (SELECT user_id FROM blacklist)

我印象最深的一个案例:有一次需要清理一批垃圾评论,条件是「用户已被封禁且评论内容包含广告链接」。当时我写的SQL是这样的:

DELETE FROM comments
WHERE user_id IN (
    SELECT user_id FROM users WHERE status = 'banned'
)
AND content LIKE '%http%'
AND created_at < '2024-01-01';

三个条件叠加,精准命中目标数据。既不会误删正常用户的评论,也不会漏掉该删的垃圾数据。

核心原则:条件越具体,操作越安全。能用复合条件就别用单一条件,能用子查询就别靠感觉。

7.4 事务保护:你的后悔药

说到事务,我得先问一句:你知道为什么要有事务吗?

说白了,事务就是给你一个「反悔」的机会。你执行了一堆UPDATE和DELETE,发现搞错了,只要还没提交,就能全部回滚。

在SQLite里,事务的基本用法是这样的:

BEGIN TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;

-- 如果一切正常
COMMIT;

-- 如果发现错了
-- ROLLBACK;

这个例子是转账操作。扣钱和加钱必须同时成功,或者同时失败。不能出现扣了钱但没加上的情况。这就是事务的原子性。

我在项目中遇到过更复杂的场景:批量删除用户数据时,需要同时清理用户表、订单表、评论表、日志表。如果其中一步失败,前面删掉的数据就找不回来了。这时候事务就是救命稻草:

BEGIN TRANSACTION;

DELETE FROM user_logs WHERE user_id = 1001;
DELETE FROM comments WHERE user_id = 1001;
DELETE FROM orders WHERE user_id = 1001;
DELETE FROM users WHERE user_id = 1001;

-- 检查每步的执行结果
-- 如果全部成功
COMMIT;

-- 如果任何一步失败
-- ROLLBACK;
经验之谈:我个人的习惯是,只要涉及多条写操作(UPDATE/DELETE/INSERT),一律放在事务里执行。哪怕只有两条语句,也值得加事务。因为你永远不知道下一秒会发生什么——数据库崩溃、网络中断、程序异常退出……事务就是你的最后一道防线。

7.5 事务的ACID特性

聊到事务,就绕不开ACID这四个字母。我尽量用大白话解释:

特性 含义 举个栗子
原子性(Atomicity) 事务里的操作要么全做,要么全不做 转账:扣钱和加钱必须同时完成
一致性(Consistency) 事务前后,数据必须符合所有规则 账户余额不能为负数
隔离性(Isolation) 多个事务互不干扰 你转账时,别人查到的余额是中间状态还是最终状态?
持久性(Durability) 事务提交后,数据不会丢失 哪怕断电了,已提交的数据也还在

嗯,这里要注意:SQLite默认是自动提交模式,每条语句独立成一个事务。如果你想手动控制,就必须显式地写BEGIN和COMMIT。

7.6 避坑指南:我踩过的那些雷

最后,分享几个我亲身踩过的坑,希望能帮你少走弯路:

  • 忘记WHERE条件:我曾经在调试时写了一条UPDATE,忘了加WHERE,结果整张表的价格全变成了同一个值。还好有备份,不然就凉了。
  • 事务没提交:有一次我执行了BEGIN,然后做了一堆更新,结果忘了COMMIT,直接关闭了程序。第二天发现数据没变,白忙活一场。
  • DELETE后没检查影响行数:我习惯在DELETE之后调用sqlite3_changes()检查实际删了多少行。如果结果是0,说明条件可能写错了,或者数据根本不存在。
  • 嵌套事务的陷阱:SQLite支持savepoint,但嵌套太深容易搞混逻辑。我的建议是:尽量保持事务扁平化,别搞太复杂。
一句话总结:UPDATE和DELETE是数据库里最危险的操作,没有之一。写之前确认条件,写之前开启事务,写之后检查结果。这三步做到位,基本不会出大问题。
数据更新与删除操作流程 开始操作 是否开启事务? BEGIN TRANSACTION 执行 UPDATE / DELETE 结果是否正确? COMMIT ROLLBACK 先确认条件 → 开启事务 → 执行操作 → 检查结果 → 提交或回滚

公众号:蓝海资料掘金营,微信deep3321