- DELETE 删除行,可以选择使用 WHERE 进行过滤。桌子还留着。
- TRUNCATE 快速删除所有行。表结构保持不变。
- DROP 删除整个表:数据、结构、索引和触发器。
- 回滚、触发器和身份重置在每个数据库中的行为都不同,因此请检查您的数据库。
测试人员想要在新的测试运行之前清除暂存表。应该是DELETE、TRUNCATE还是DROP?错误的选择可能意味着工作缓慢、缺少桌子,或者在生产中意味着一个非常糟糕的下午。
我们在 SQLite 3.50 中运行了下面的 DELETE 和 DROP 示例,输出来自该运行。 SQLite 没有 TRUNCATE 命令,因此 TRUNCATE 注释基于 PostgreSQL、MySQL 和 SQL Server 官方文档,而不是我们自己的测试。
简短的回答
- 需要删除一些行?使用
DELETE ... WHERE。 - 需要清空表但保留它吗?在支持的情况下使用
TRUNCATE。 - 桌子需要永久消失吗?使用
DROP TABLE。
删除:删除行
DELETE 是数据更改 (DML) 命令。它一次删除一行,触发行级触发器并且可以进行过滤。我们设置了一个带有审计触发器的小表:
CREATE TABLE logs (id INTEGER PRIMARY KEY AUTOINCREMENT, level TEXT);
INSERT INTO logs (level) VALUES ('INFO'),('ERROR'),('INFO'),('DEBUG');
CREATE TABLE audit (msg TEXT);
CREATE TRIGGER logs_delete AFTER DELETE ON logs
BEGIN INSERT INTO audit VALUES ('deleted ' || OLD.id); END;
DELETE FROM logs WHERE level = 'DEBUG';
SELECT id, level FROM logs; -- (1,'INFO'), (2,'ERROR'), (3,'INFO')
SELECT * FROM audit; -- 'deleted 4'
只有匹配的行出现,并且触发器记录了它。运行不带 WHERE 子句的 DELETE FROM logs 删除了其他三行,然后审计表保存了四个条目,每个删除的行一个。
DELETE 不会重置自动递增计数器。清空表后,我们的下一个插入收到 id 5,而不是 1。在 SQLite 中,这是因为 AUTOINCREMENT。 MySQL InnoDB 和 SQL Server 标识列也会在删除后从原来的位置继续。
提示: 在任何带有 WHERE 子句的 DELETE 之前,首先运行与 SELECT COUNT(*) 相同的子句。如果计数让您感到惊讶,请停止。请阅读 我们的 SQL NULL 值指南,了解 NULL 在 WHERE 子句中创建的陷阱。
TRUNCATE:清空表
TRUNCATE TABLE logs; 一次删除每一行。它不能采用 WHERE 子句。数据库通常不会逐行删除,而是释放表的数据页,这就是它在大型表上速度更快的原因。
当我们在 SQLite 中运行它时,我们收到语法错误,因为 SQLite 没有命令:
TRUNCATE TABLE logs;
-- near "TRUNCATE": syntax error
在 SQLite 中,使用 DELETE FROM logs; 且不带 WHERE 子句。 SQLite 的文档描述了一种“截断优化”,当表没有触发器时,它可以加快速度。我们的表有一个触发器,因此删除是逐行运行的,并为每一行触发它。
在支持 TRUNCATE 的数据库中,通过其文档:
- 它通常不会触发行级 DELETE 触发器。
- 它通常会重置身份或自动增量计数器,例如在 MySQL 和 SQL Server 中。在 PostgreSQL 中,您添加
RESTART IDENTITY。 - 在大多数系统中它需要比 DELETE 更强的权限。
DROP:删除表本身
DROP TABLE 是一个架构 (DDL) 命令。它删除数据、表定义、索引、触发器和约束。引用该表的任何内容(例如视图或外键)也会受到影响。
DROP TABLE logs;
SELECT * FROM logs;
-- no such table: logs
DROP TABLE IF EXISTS logs; -- no error, even though it is gone
IF EXISTS 在可能运行多次的安装脚本中很方便。正如我们在 SQL 视图解释 中所示,已删除表上的视图可能会损坏。
对照表
| 特点 | 删除 | 截断 | 丢弃 |
|---|---|---|---|
| 删除 | 部分或全部行 | 所有行 | 表和行 |
| WHERE 子句 | 是的 | 否 | 否 |
| 命令类型 | 数据管理语言 | DDL(通常) | 数据定义语言 |
| 行触发火灾 | 是的 | 通常没有 | 否 |
| 重置身份 | 否 | 通常是的 | 桌子不见了 |
| 大桌子上的速度 | 慢 | 快 | 快 |
| 桌子仍然存在 | 是的 | 是的 | 否 |
可以回滚吗?
这是很多文章过于简单化的地方。诚实的答案是这取决于数据库。
DELETE 可以在任何地方的事务内回滚。我们测试了它:
BEGIN;
DELETE FROM logs;
SELECT COUNT(*) FROM logs; -- 0
ROLLBACK;
SELECT COUNT(*) FROM logs; -- 3
DROP也在SQLite中回滚,它支持事务性DDL:
BEGIN;
DROP TABLE logs;
ROLLBACK;
SELECT COUNT(*) FROM logs; -- 1 (the table is back)
对于其他数据库中的 TRUNCATE 和 DROP,根据其文档:
| 数据库 | 事务中的 TRUNCATE / DROP |
|---|---|
| PostgreSQL | 可以回滚。 DDL 是事务性的。 |
| SQL服务器 | 可以在显式事务内回滚。 |
| MySQL | 导致隐式提交,因此无法回滚。 |
| 甲骨文 | 隐式提交。 DROP 可以从回收站中恢复。 |
所以“TRUNCATE永远不能回滚”对于PostgreSQL和SQL Server来说是错误的,而“它总是可以回滚”对于MySQL来说是错误的。在事务之外,如果没有备份,它们都无法撤消。
为什么 TRUNCATE 很快
DELETE 记录每个删除的行,以便可以撤消和复制更改、触发触发器并逐行检查约束。在具有数百万行的表上,这会产生大量工作和日志量。 TRUNCATE 通常会在一次操作中取消分配存储,而日志记录要少得多。要删除较大的子集而不是所有内容,请分批删除,以避免长时间锁定。我们在 SQL 查询优化 中介绍了锁定和查询成本。
外键和权限
- 外键。 SQL Server 拒绝 TRUNCATE 外键引用的表。 PostgreSQL 也要求您截断引用表,或者使用
CASCADE。相反,DELETE 根据约束检查每一行。 - 权限。 DELETE需要删除权限。 TRUNCATE 通常需要 ALTER 或专用的 TRUNCATE 权限,而 DROP 需要模式的所有权或 ALTER。
- 复制和审计。如果其他系统依赖于行级更改日志或触发器,则 TRUNCATE 可能会跳过它们。首先与拥有这些系统的人核实。
使用哪一个
| 情况 | 使用 |
|---|---|
| 删除客户已取消的订单 | 使用 WHERE 删除 |
| 每次测试运行前重置暂存表 | TRUNCATE(或 SQLite 中的 DELETE) |
| 保持审核触发器正常工作 | 删除 |
| 删除设计中不再存在的表格 | DROP 已审核的迁移 |
| 清除数百万条旧行,但保留最近的行 | 批量删除,或者分区并删除旧分区 |
对于测试数据,TRUNCATE 很受欢迎,因为它会重置 ID,从而使测试可重复。将每个测试包装在回滚的事务中是另一种选择,SQL 事务和 ACID 中对此进行了解释。
安全检查表
- 确认您连接到哪个数据库和环境。
- 首先将 WHERE 子句作为 SELECT 运行。
- 将更改包装在数据库支持的事务中。
- 在生产中 TRUNCATE 或 DROP 之前进行最近的、经过测试的备份。
- 将 DROP 语句放在经过审查的迁移脚本中,而不是放在临时控制台中。
- 在删除表之前检查依赖于表的内容,例如视图、外键和报告。
安全删除大量数据
使用一次 DELETE 删除数百万行可能会长时间锁定表、填满事务日志并减慢复制速度。常见的模式是小批量删除并重复,直到什么都不剩:
-- SQL Server style (not run for this article)
DELETE TOP (5000) FROM logs WHERE created_at < '2026-01-01';
-- PostgreSQL style (not run for this article)
DELETE FROM logs
WHERE id IN (SELECT id FROM logs WHERE created_at < '2026-01-01' LIMIT 5000);
循环运行该语句,在批次之间短暂暂停,直到报告零行。每个批次都是一个小事务,因此锁很短并且其他查询继续运行。过滤器列上的索引(此处 created_at)可保持每个批次的速度。
对于按日期增长的数据(例如日志和事件),表分区 甚至更好。每个月都位于自己的分区中,删除旧数据成为一种快速操作,删除或截断一个分区,而不是逐行删除。
软删除
许多应用程序从不实际删除业务记录。他们添加一列(例如 deleted_at)并进行设置,然后查询 deleted_at IS NULL 进行过滤。软删除使恢复变得容易并保留审计历史记录,但每个查询都必须记住过滤器,这对于视图来说是一项很好的工作。唯一约束也需要小心,因为软删除的行仍然保留其值。隐私法可能要求在一段时间后真正删除个人数据,因此软删除不能替代保留策略。
最后的话
DELETE 是精确、仔细的工具。 TRUNCATE 是快速重置按钮,DROP 会完全删除表。最大的风险是假设一个数据库的行为与另一个数据库相似,尤其是在回滚方面。如有疑问,请首先在您自己的数据库中的表副本上测试该命令。
常见问题
DELETE 和 TRUNCATE 之间的主要区别是什么?
DELETE 逐行删除行,支持 WHERE 并触发触发器。 TRUNCATE 一次删除所有行,速度更快,通常会跳过行触发器并经常重置身份计数器。
TRUNCATE 可以回滚吗?
在 PostgreSQL 和 SQL Server 中,是的,在事务内。在 MySQL 和 Oracle 中,它是隐式提交的,因此无法回滚。
DELETE是否重置自增值?
不会。删除后,新行会从前一个计数器继续。在我们的 SQLite 测试中,删除所有行后下一个 ID 是 5。
SQLite 支持 TRUNCATE 吗?
不可以。使用不带 WHERE 子句的 DELETE FROM 表。当表没有触发器时,SQLite 会对此进行优化。
DROP TABLE 删除了什么?
数据、表定义、索引、触发器和约束。依赖于表的视图和外键可能会破坏或阻止删除。
继续学习
需要测试、WordPress 或 SEO 方面的帮助?
我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。
更多指南 测试、人工智能和现代开发
喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。
人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它
人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。
阅读文章 →DevOps环境变量解释:.env 文件、秘密和配置正确完成
使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。
阅读文章 →软件开发API 速率限制解释:算法、标头和处理 429 错误
API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。
阅读文章 →测试自动化Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误
使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。
阅读文章 →软件测试代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义
用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。
阅读文章 →数据库与 SQLSQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们
DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。
阅读文章 →数据库与 SQLSQL 视图解释:创建、使用和避免陷阱(附示例)
SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。
阅读文章 →软件开发TypeScript 接口与类型:真正的差异,使用编译器进行测试
TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。
阅读文章 →软件开发Python *args 和 **kwargs 解释:灵活的函数和清晰的示例
Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。
阅读文章 →软件开发Python Lambda 函数:语法、实际用途以及何时不使用它们
Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。
阅读文章 →人工智能什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环
什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。
阅读文章 →DevOps监控与可观察性:真正的区别,包括日志、指标和跟踪
监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。
阅读文章 →DevOpsDocker Volumes 与 Bind Mounts:以正确的方式保留容器数据
Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。
阅读文章 →测试自动化TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式
TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。
阅读文章 →软件开发Git merge 与 rebase 的区别:示例及如何选择
用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。
阅读文章 →软件开发Python 列表推导式:语法、示例与何时避免使用
Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。
阅读文章 →数据库与 SQLSQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)
通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。
阅读文章 →