7、UPDATE与DELETE操作:更新记录、删除记录、事务保护下的修改、级联操作

好,咱们今天聊聊数据库里最“危险”的两个操作——UPDATE和DELETE。说它们危险,是因为一旦执行错了,数据就没了,或者变得乱七八糟。我刚开始做嵌入式数据库开发那会儿,就吃过这个亏。有一次在产线上跑批量更新,少写了一个WHERE条件,结果整张表的价格字段全变成了同一个数……嗯,从那以后,我写UPDATE和DELETE之前,都会先看一眼WHERE子句,确认三遍。

7.1 更新记录:UPDATE 语句的细节

UPDATE说白了就是改数据。语法很简单,但坑不少。

UPDATE 表名 SET 列1 = 值1, 列2 = 值2 WHERE 条件;

我个人习惯,写UPDATE之前,先用SELECT把要改的行查出来看一眼。比如:

-- 先查
SELECT * FROM products WHERE product_id = 101;

-- 确认无误后再改
UPDATE products SET price = 29.99 WHERE product_id = 101;

你想想看,如果直接写UPDATE,万一条件写错了,连后悔药都没得吃。在SQLite里,UPDATE默认是不支持回滚的——除非你开了事务。这个咱们后面细说。

小技巧: 在嵌入式环境里,UPDATE尽量带上LIMIT(如果数据库支持),防止一次更新太多行导致锁表时间过长。

7.2 删除记录:DELETE 与 TRUNCATE 的区别

DELETE和TRUNCATE都能删数据,但本质完全不同。

操作 特点 能否回滚 速度
DELETE 逐行删除,触发触发器 事务内可回滚 慢(记录日志)
TRUNCATE 直接释放数据页,不逐行删 通常不可回滚 极快

我在项目中遇到过一个问题:用DELETE删了10万条记录,结果因为没开事务,删到一半断电了,数据处于半删不删的状态,恢复起来特别麻烦。后来我学乖了,批量删除一定包在事务里。

BEGIN TRANSACTION;
DELETE FROM logs WHERE timestamp < '2023-01-01';
-- 检查影响行数
SELECT changes();
-- 如果影响行数不对,就 ROLLBACK
ROLLBACK;  -- 或者 COMMIT;
警告: 在SQLite里,TRUNCATE不是标准SQL语句。你要清空表,得用 DELETE FROM 表名 或者 DROP表再重建。千万别以为TRUNCATE在所有嵌入式数据库里都通用。

7.3 事务保护下的修改:原子性才是王道

事务这东西,说白了就是“要么全做,要么全不做”。我经常跟团队里的新人说:任何涉及多条记录的修改,都必须包在事务里

举个例子,你要把A账户扣100块,B账户加100块。这两条UPDATE必须在一个事务里:

BEGIN TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 假设这里检查一下余额是否足够
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- 如果一切正常
COMMIT;

-- 如果中间出错了
-- ROLLBACK;

为什么会这样?因为如果第一条UPDATE成功了,第二条失败了,钱就凭空消失了。事务就是用来防止这种“半截子操作”的。

核心原则: 在嵌入式数据库里,事务的隔离级别通常默认是“可串行化”或“读已提交”。但SQLite默认是“可串行化”,这意味着写操作会互斥。高并发场景下要注意性能。

7.4 级联操作:外键约束下的自动更新与删除

级联操作,就是当你改了主表的记录,从表自动跟着变。比如你删了一个客户,他所有的订单也应该被删掉——这就是级联删除。

定义外键时加上 ON DELETE CASCADE 或 ON UPDATE CASCADE:

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    customer_id INTEGER,
    amount REAL,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

这样,当你执行:

DELETE FROM customers WHERE customer_id = 5;

所有 customer_id = 5 的订单也会被自动删除。听起来很方便对吧?但我要提醒你:级联操作一定要谨慎使用。我曾经见过一个项目,因为级联删除设计得太“激进”,删一个用户结果连带删了上千条关联记录,最后数据恢复花了两天。

避坑指南: 在SQLite里,外键约束默认是关闭的!你必须每次连接数据库后执行 PRAGMA foreign_keys = ON; 才能启用。我刚开始用SQLite时不知道这个,级联操作怎么都不生效,排查了半天才发现是这个问题。

7.5 知识体系总览

下面这张图,把本章的核心逻辑串起来了。你可以把它当作一个“操作决策流程图”:

UPDATE / DELETE 操作决策流程 开始修改数据 是否单行修改? 直接 UPDATE/DELETE 必须使用事务 有外键关联? 启用级联:CASCADE 手动处理关联数据

这张图的核心逻辑是:先判断影响范围,再决定是否用事务,最后考虑外键级联。每一步都走对了,数据安全就有保障。

7.6 实战建议总结

  • 写UPDATE/DELETE前,先SELECT确认——这是成本最低的防错手段。
  • 批量操作必须包事务——哪怕只是删10条记录,也养成习惯。
  • 级联操作要克制——能用代码逻辑处理的,尽量别依赖数据库级联。
  • SQLite记得开外键——PRAGMA foreign_keys = ON; 这句话写进初始化代码里。

嗯,关于UPDATE和DELETE,今天就聊这么多。这些操作看起来简单,但实际项目中出问题最多的就是它们。记住一句话:数据修改前多花一分钟确认,可能省下后面一整天恢复数据的时间


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