DELETE:删除数据
本教程共 50 篇 · 第 22 篇 · 更新于 2026-07-31
22. DELETE:删除数据
本节目标:学完本章你能用 DELETE 删除一行、多行或全部行,清楚”漏写 WHERE 会清空整表”这一致命风险,并知道用事务给危险操作上保险。
和 INSERT 新增、UPDATE 修改并列,DELETE 负责”把整行记录抹掉”。它比 UPDATE 更决绝:UPDATE 至少还留着这一行,DELETE 是直接让这一行从表里消失。正因为不可逆性强,它也是最需要小心的写操作。
DELETE 的基本形态很简单:
DELETE FROM users
WHERE id = 7;
FROM 后面跟表名,WHERE 指定”删哪些行”。这条语句只删除 id = 7 的那一行。可以结合 AND/OR、LIKE 等删除多行,比如”删掉所有年龄大于 60 的用户”:
DELETE FROM users
WHERE age > 60;
这和 WHERE 的写法完全一致(第 16 章学过),区别只在于 DELETE 删除的是满足条件的整行,而不是改某个列。
DELETE 最著名、也最致命的坑,就是漏写 WHERE。看这句:
DELETE FROM users;
没有 WHERE,SQLite 会删除表里所有行——整张表被清空,而且默认情况下这极难找回。所以 DELETE 和 UPDATE 共享同一条铁律:动刀前先 SELECT 预览。
-- 先确认会删哪些
SELECT * FROM users WHERE age > 60;
-- 确认无误再删除
DELETE FROM users WHERE age > 60;
一个好消息是:当 DELETE 不带 WHERE、且表上没有触发器时,SQLite 会走”truncate 优化”,一次性把全部行清掉(而不是真的逐行访问),速度很快。但这只是”快”,并不等于”安全”——数据照样没了。如果你只是想清空一张表,也可以考虑用其它方式(视场景可能是 DROP 后重建,或后续章节讲到的删表/重建策略),但无论哪种,动手前都要想清楚。
危险操作怎么上保险?用事务(Transaction)。把 DELETE 包在 BEGIN 和 COMMIT 之间,执行后先 SELECT 检查,发现删错了就 ROLLBACK 回滚,数据原样回来:
BEGIN;
DELETE FROM users WHERE age > 60;
-- 检查一下,发现删多了
ROLLBACK;
事务的完整机制留到第 36 章细讲,但这里先记住这个保命口诀:要删大量数据,先 BEGIN,确认无误再 COMMIT,拿不准就 ROLLBACK。注意 DELETE 本身不会自动开启事务回滚,必须你自己用 BEGIN/ROLLBACK 兜住。
再说外键。如果 orders 表的 user_id 引用了 users 的 id,而你删掉了一个”还有订单的用户”,会发生什么?默认情况下 SQLite 的外键约束是关闭的,于是父行被删,子表里那些订单变成了”指向不存在用户的孤儿记录”,数据关系就此断裂。想让 SQLite 在删除父行时自动处理子表(比如级联删掉子行、或拒绝删除),必须先执行 PRAGMA foreign_keys = ON,并且每次新连接都要重新打开这个开关。
PRAGMA foreign_keys = ON;
DELETE FROM users WHERE id = 1; -- 若 orders 里有其订单且设了 ON DELETE CASCADE,则子行一并删除
重点提示
DELETE 删的是”整行”,不是某个列。如果只想清空某一列的值,应该用 UPDATE 把该列 SET 为 NULL,而不是 DELETE。删全部行可用不带 WHERE 的 DELETE,但请分清”清空数据”和”删表结构”是两回事。
实用技巧
任何 DELETE 前先 BEGIN 开启事务,删完 SELECT 核查,没问题再 COMMIT;一旦不对立刻 ROLLBACK。这是生产环境删数据的标准自我保护动作。
常见坑
漏写 WHERE 的 DELETE FROM 会清空整张表,且默认不可轻易恢复!执行前务必用相同条件的 SELECT 预览。另外,删除被外键引用的父行时,SQLite 默认不强制参照完整性(外键默认关闭),需 PRAGMA foreign_keys = ON 才会触发级联或拒绝,否则会留下孤儿数据。
和 UPDATE 一样,SQLite 3.35.0+ 也支持 DELETE … RETURNING,删完直接回显被删的行,方便做审计日志:
DELETE FROM users WHERE id = 7 RETURNING name, email;
另一个工程上极重要的对比是”硬删除”与”软删除”。DELETE 是硬删除——数据物理消失,恢复极难。真实系统里,很多”删除”其实是用 UPDATE 把一行标记为 is_deleted = 1(软删除),数据还在,只是查询时过滤掉。软删除的好处是能恢复、能留审计痕迹;代价是查询都要带”未删除”条件、数据会越积越多。该硬删还是软删,取决于这条数据删了是否还需追溯——订单、账目通常软删,临时缓存、测试数据才硬删。记住这个取舍,能让你少踩无数”删了才发现还要用”的坑。
还有 DELETE 与 DROP TABLE 别搞混:DELETE 删的是”行”,表结构、索引都还在,之后还能继续 INSERT;DROP TABLE 是把整张表(连同结构)连根拔掉,二者量级天差地别,动手前看清楚你敲的是哪个命令。清空数据用 DELETE(或后续章节的清空方式),删表才用 DROP。
适用场景小结:用户注销、订单取消、过期日志清理、测试数据回滚。无论哪种,请默念三件套——带 WHERE、先 SELECT 预览、BEGIN 事务兜底——再去按回车。这三条只占你十秒钟,却可能救回一整张表的数据。
最后强调备份意识:SQLite 是 serverless 的单文件数据库,整个库就是一个 .db 文件。这意味着”备份”极其简单——直接复制这个文件即可;也意味着一旦 DELETE 误清空,若既没有事务兜底、也没有文件备份,数据就真的找不回来了。对关键库,删数据前先 cp 一份文件,是成本最低的一道保险。养成”改前 BEGIN、删前备份文件”的习惯,比任何事后补救技巧都管用。
一句话收尾:DELETE 的破坏力与它的语法简洁度成正比——写得越轻松,出事越容易。永远假设”下一行就会误删”,用 WHERE、预览、事务三道闸门把自己拦住。对重要数据,删之前备份文件,删之后核对结果,这两步能挡掉绝大多数事故。
类比小结:DELETE 像从通讯录里撕掉一页——撕之前先确认”是不是这一页”,撕完那一页就不在了。最怕的是”想撕一页却把整本通讯录甩进碎纸机”(漏写 WHERE)。给碎纸机套个”事务拉链袋”:先 BEGIN 装进去,确认无误再封口(COMMIT),后悔了拉开拉链倒出来(ROLLBACK),你就永远有退路。
什么时候用 DELETE?用户注销账号、订单超时关闭、日志过期清理、测试数据回滚……凡是要”让记录彻底消失”的场景都用它。但请永远带着 WHERE、带着预览、带着事务三件套上场。