前言

对于drop、truncate和delete,虽然简单,但是真要使用或者面试时候问到还是需要有一定的总结,自己也比较懒,前面很多人总结过了,但是并不完善,因此参看多篇文章之后进行一个总结。在此之前先简单了解下什么是DDL和DML。
DDL(数据定义语言,Data Definition Language):DDL代表数据定义语言,是一种有助于创建数据库模式的SQL命令。DDL中常用的命令有:createdropaltertruncaterename等等。
DML(数据操作语言,Data Manipulation Language):DML代表数据操作语言,是一种有助于检索和管理关系数据库中数据的SQL命令。DML中常用的命令有:insertupdatedeleteselect等等。

1、delete

delete可根据条件删除表中满足条件的数据,如果不指定where子句,那么删除表中所有记录。delete有两种用法,一种是带where条件删除一个范围内的数据,另一种则是不带where条件全部删除。如:delete from table_name where xxxdelete from table_name。delete是DML,只删除数据不删除表的结构,会走事务,执行时会触发trigger。也就是说,这个操作会被放到rollback segment中,事务提交之后才会生效,如果有相应的触发器trigger,那么执行的时候可以被触发。执行delete操作时,每次从表中删除一行,并且同时将该行的的删除操作记录在redo和undo表空间中以便进行回滚(rollback)和重做操作,但要注意表空间要足够大,需要手动提交(commit)操作才能生效,可以通过rollback撤消操作。

  • delete语句不影响表所占用的extent(就是表结构的中的区),高水线(high watermark)保持原位置不变。 (高水位线就存在于段(segment)中,它用于标识段中已使用过的数据块与未使用的数据块二者间交界,扫描表数据的时候,高水位线以下的所有数据块都必须被扫描。)

  • 在 InnoDB 中,delete其实并不会真的把数据删除,mysql 实际上只是给删除的数据打了个标记为已删除,因此 delete 删除表中的数据时,表文件在磁盘上所占空间不会变小,存储空间不会被释放,只是把删除的数据行设置为不可见。虽然未释放磁盘空间,但是下次插入数据的时候,仍然可以重用这部分空间(重用 → 覆盖)。

  • delete执行时,会先将所删除数据缓存到rollback segement中,事务commit之后生效;delete from table_name删除表的全部数据,对于MyISAM 会立刻释放磁盘空间,InnoDB 不会释放磁盘空间;
    对于delete from table_name where xxx带条件的删除, 不管是InnoDB还是MyISAM都不会释放磁盘空间;
    delete操作以后使用 optimize table table_name则会立刻释放磁盘空间。不管是InnoDB还是MyISAM 。所以要想达到释放磁盘空间的目的,delete以后执行optimize table操作。

2、truncate

truncate的作用是清空表或者说是截断表,并且重置auto_increment的值。只能作用于表。truncate的语法很简单,后面直接跟表名即可,例如:truncate table table_name或者 truncate table_name。其作用是清空表中的数据, truncate和不带where子句的delete作用相同。truncate是DDL,即操作会立即生效,原数据不会放到rollback segment中,不能回滚也不会触发触发器

执行后立即生效,无法找回
执行后立即生效,无法找回
执行后立即生效,无法找回

  • truncate会立刻释放磁盘空间 ,不管是 InnoDB和MyISAM 。truncate table其实有点类似于drop table 然后creat,只不过这个create table 的过程做了优化,比如表结构文件之前已经有了等等。所以速度上应该是接近drop table的速度。

  • truncate速度快,而且效率高是因为使用的系统和事务日志资源少。delete语句每次删除一行,并在事务日志中为所删除的每行记录一项truncate通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。

  • truncate会删除表中所有记录,并且将重新设置高水线和所有的索引(意思就是truncate会删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子),缺省情况下将空间释放到minextents的extent(就是表结构中的段内的区域),除非使用reuse storage(使用这句话,所在的extent空间不会被回收,只是将数据删除掉,数据删除之后的freespace空间,只能供本表使用,其他的不可以使用)。不会记录日志,所以执行速度很快,但不能通过rollback撤消操作(如果一不小心把一个表truncate掉,也是可以恢复的,只是不能通过rollback来恢复)。

  • 对于外键(foreign key )约束引用的表,不能使用 truncate table(会报错Cannot truncate a table referenced in a foreign key constraint),也不能使用drop table(会报错Cannot delete or update a parent row: a foreign key constraint fails),而应使用不带 where 子句的 delete 语句。此外,truncate table不能用于参与了索引视图的表。

  • 对于MyISAM,truncate会重置auto_increment(自增序列)的值为1。而delete后表仍然保持auto_increment。对于InnoDB,truncate会重置auto_increment的值为1。delete后表仍然保持auto_increment。但是在做delete整个表之后重启MySQL的话,则重启后的auto_increment会被置为1。也就是说,InnoDB的表本身是无法持久保存auto_increment。delete表之后auto_increment仍然保存在内存,但是重启后就丢失了,只能从1开始。实质上重启后的auto_increment会从 select 1+max(i_col) from table_name开始,i_col为某列字段名。

执行truncate语句需要拥有表的drop权限,从逻辑上讲,truncate table类似于delete删除所有行的语句或drop table然后再create table语句的组合。为了实现高性能,它绕过了删除数据的DML方法,因此,它不能回滚。尽管truncate table与delete相似,但它被分类为DDL语句而不是DML语句。

3、drop

drop作用是删除表的结构(元数据)和表数据,其用法为:drop table table_name,drop是DDL,会隐式提交,所以,不能回滚,不会触发触发器。并且drop语句删除表结构所有数据,并将表所占用的空间全部释放
执行后立即生效,无法找回
执行后立即生效,无法找回
执行后立即生效,无法找回

  • drop语句将删除表的结构所依赖的约束,触发器,索引,依赖于该表的存储过程/函数将保留,但是变为invalid状态。

4、总结

在速度上,一般来说,drop> truncate > delete

  • 如果想删除部分数据用delete,注意带上where子句,回滚段要足够大;如果想删除表,当然用drop;
  • 如果想保留表而将所有数据删除,如果和事务无关(不能回滚),用truncate即可;
  • 如果和事务有关,或者想触发trigger,还是用delete;
  • 如果是整理表内部的碎片,可以用truncate跟上reuse stroage,再重新导入/插入数据。
  • truncate与drop是DDL语句,执行后无法回滚;delete是DML语句,可回滚。
  • truncate只能作用于表;delete,drop可作用于表、视图等。
  • truncate会清空表中的所有行,但表结构及其约束、索引等保持不变;drop会删除表的结构及其所依赖的约束、索引等。
  • truncate会重置表的自增值;delete不会。
  • truncate不会激活与表有关的删除触发器;delete可以。
  • truncate后会使表和索引所占用的空间会恢复到初始大小;delete操作不会减少表或索引所占用的空间,drop语句将表所占用的空间全释放掉。

5、参考

1、Delete、Truncate、Drop有啥区别?我该如何选择呢?
2、Truncate用法详解
3、drop、truncate和delete的区别
4、详解SQL中drop、delete和truncate的异同

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐