SQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

  1. 首页
  2. 博客
  3. 数据库和 SQL

数据库与 SQL

DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

所有三个命令都会删除某些内容,但它们以不同的方式删除不同的内容。以下是每个规则的作用、可以撤消的操作以及规则在数据库之间的变化方式。

阅读约 8 分钟
SQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

要点速览

  • 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 中对此进行了解释。

安全检查表

  1. 确认您连接到哪个数据库和环境。
  2. 首先将 WHERE 子句作为 SELECT 运行。
  3. 将更改包装在数据库支持的事务中。
  4. 在生产中 TRUNCATE 或 DROP 之前进行最近的、经过测试的备份。
  5. 将 DROP 语句放在经过审查的迁移脚本中,而不是放在临时控制台中。
  6. 在删除表之前检查依赖于表的内容,例如视图、外键和报告。

安全删除大量数据

使用一次 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 删除了什么?

数据、表定义、索引、触发器和约束。依赖于表的视图和外键可能会破坏或阻止删除。

继续学习

与Stack Quora合作

需要测试、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%覆盖率仍然会错过错误,以及合理的目标。

阅读文章 →
数据库与 SQL

SQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。

阅读文章 →
数据库与 SQL

SQL 视图解释:创建、使用和避免陷阱(附示例)

SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。

阅读文章 →
软件开发

TypeScript 接口与类型:真正的差异,使用编译器进行测试

TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。

阅读文章 →
软件开发

Python *args 和 **kwargs 解释:灵活的函数和清晰的示例

Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。

阅读文章 →
软件开发

Python Lambda 函数:语法、实际用途以及何时不使用它们

Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。

阅读文章 →
人工智能

什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环

什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。

阅读文章 →
DevOps

监控与可观察性:真正的区别,包括日志、指标和跟踪

监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。

阅读文章 →
DevOps

Docker 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 循环更合适。

阅读文章 →
数据库与 SQL

SQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)

通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。

阅读文章 →