在我工作的系统中,有很多使用临时表的存储过程和 SQL 脚本。使用这些表后,最好删除它们。
我的许多同事(几乎所有人都比我更有经验)通常这样做:
TRUNCATE TABLE #mytemp
DROP TABLE #mytemp
DROP TABLE
我通常在我的脚本中使用一个。
TRUNCATE
在 a之前立即执行 a 有什么好的理由DROP
吗?
在我工作的系统中,有很多使用临时表的存储过程和 SQL 脚本。使用这些表后,最好删除它们。
我的许多同事(几乎所有人都比我更有经验)通常这样做:
TRUNCATE TABLE #mytemp
DROP TABLE #mytemp
DROP TABLE
我通常在我的脚本中使用一个。
TRUNCATE
在 a之前立即执行 a 有什么好的理由DROP
吗?
不。
TRUNCATE
并且DROP
在行为和速度上几乎相同,因此根本不需要在 aTRUNCATE
之前执行正确的操作。DROP
注意:我从 SQL Server 的角度编写了这个答案,并假设它同样适用于 Sybase。看来,情况并非完全如此。
注意:当我第一次发布这个答案时,还有其他几个评价很高的答案——包括当时接受的答案——提出了几个错误的声明,例如:
TRUNCATE
未记录;TRUNCATE
无法回滚;TRUNCATE
比DROP
;快 等等既然已经清理了这个线程,那么接下来的反驳似乎与原始问题相切。我把它们留在这里作为其他人想要揭穿这些神话的参考。
有一些流行的谎言——甚至在有经验的 DBA 中也很普遍——可能促成了这种
TRUNCATE-then-DROP
模式。他们是:TRUNCATE
没有记录,因此无法回滚。TRUNCATE
比DROP
。让我反驳这些谎言。我是从 SQL Server 的角度写这篇反驳文章的,但我在这里所说的一切都应该同样适用于 Sybase。
TRUNCATE 已记录,并且可以回滚。
TRUNCATE
是一个记录的操作,所以它可以回滚。只需将其包装在事务中即可。但是请注意,这不适用于 Oracle。尽管由 Oracle 的撤消和重做功能记录和保护,但
TRUNCATE
其他 DDL 语句不能由用户回滚,因为 Oracle在所有 DDL 语句之前和之后立即发出隐式提交。TRUNCATE
是最小记录,而不是完全记录。这意味着什么?说你TRUNCATE
一张桌子。无需将每个已删除的行都放入事务日志中,TRUNCATE
只需将它们所在的数据页标记为未分配。这就是为什么它这么快。这也是您无法TRUNCATE
使用日志阅读器从事务日志中恢复 -ed 表的行的原因。您会发现所有这些都是对已释放数据页的引用。将此与
DELETE
. 如果您DELETE
将表中的所有行都提交并提交事务,理论上您仍然可以在事务日志中找到已删除的行并从那里恢复它们。那是因为DELETE
将每个已删除的行写入事务日志。对于大型表,这将使其比TRUNCATE
.DROP与 TRUNCATE 一样快。
TRUNCATE
,DROP
是一个最少记录的操作。 这意味着DROP
也可以回滚。这也意味着它的工作方式与TRUNCATE
.DROP
将相应的数据页标记为未分配,并将表的元数据标记为已删除,而不是删除单个行。因为
TRUNCATE
和DROP
工作方式完全相同,它们运行速度一样快。 在-ing 表格之前-ing 表格是没有意义的。TRUNCATE
DROP
如果您不相信我,请在您的开发实例上运行此演示脚本。在我的本地机器上,有一个温暖的缓存,我得到的结果如下:
因此,对于一个 1.34亿行的表来说
DROP
,TRUNCATE
实际上根本不需要任何时间。(在冷缓存上,第一次或两次运行大约需要 2-3 秒。)我还认为TRUNCATE
thenDROP
操作的平均持续时间较长是由于我的本地机器上的负载变化,而不是因为这种组合在某种程度上是神奇的比单个操作差一个数量级。毕竟,它们几乎完全相同。如果您对这些操作的日志记录开销的更多细节感兴趣,Martin 对此有一个简单的解释。
TRUNCATE
然后进行测试DROP
与DROP
直接进行测试表明,第一种方法实际上略微增加了日志记录开销,因此甚至可能会适得其反。查看各个日志记录显示
TRUNCATE ... DROP
版本几乎与DROP
版本相同,除了有这些额外的条目。所以第
TRUNCATE
一个版本最终浪费了一些精力来对各种系统表进行一些更新,如下所示rcmodified
所有表列sys.sysrscols
rcrows
于sysrowsets
pgfirst
,pgroot
,pgfirstiam
,pcused
,pcdata
,pcreserved
insys.sysallocunits
只有在下一条语句中删除表时,这些系统表行才会最终被删除。
TRUNCATE
下面是对 vs执行的日志记录的完整分解DROP
。我还添加DELETE
了用于比较目的。该测试是在具有完全恢复模型的数据库中针对每页一行的 1,000 行表进行的。由于根索引页和 3 个中间级索引页,该表总共消耗了 1,004 页。
其中 8 个页面是混合范围中的单页面分配,其余的分布在 125 个统一范围中。8 个单页取消分配显示为 8
LOP_MODIFY_ROW,LCX_IAM
个日志条目。125 个盘区解除分配为LOP_SET_BITS LCX_GAM,LCX_IAM
. 这两个操作还需要更新相关PFS
页面,因此合并了 133LOP_MODIFY_ROW, LCX_PFS
个条目。然后,当实际删除表时,需要从各种系统表中删除有关它的元数据,因此有 22 个系统表LOP_DELETE_ROWS
日志条目(如下所示)下面是完整的脚本
好的,我想我会尝试做一些不依赖任何“热缓存”的基准测试,这样希望它们会是一个更现实的测试(也使用 Postgres,看看它是否与其他发布的答案的相同特征相匹配) :
我的基准测试使用带有大型数据库的 postgres 9.3.4,(希望足够大,不适合 RAM 缓存):
使用这个测试数据库脚本:https ://gist.github.com/rdp/8af84fbb54a430df8fc0
有 10M 行:
有 100M 行:
因此,据此我推测如下: drop 与 truncate+drop 一样快(或更快)(至少对于现代版本的 Postgres 而言),但是,如果您还计划转身并重新创建表格,您可以坚持做直线截断,这比 drop+recreate 更快(有道理)。FWIW。
注意1:https ://stackoverflow.com/questions/11419536/postgresql-truncation-speed/11423886#11423886 (说postgres 9.2可能比以前的版本截断更快)。与往常一样,使用您自己的系统进行基准测试以查看其特征。
注2:如果在事务中,截断可以在postgres中回滚: http ://www.postgresql.org/docs/8.4/static/sql-truncate.html
注3:截断可以,小表,有时比删除慢:https ://stackoverflow.com/questions/11419536/postgresql-truncation-speed/11423886#11423886
添加一些历史观点...
删除一个表需要更新多个系统表,而这通常需要在单个事务中进行这些系统表更改(想想“开始传输、删除 syscolumns、删除 sysobjects、提交”)。
“删除表”中还包括需要解除分配与表关联的所有数据/索引页。
很多很多很多年前......空间释放过程包含在还更新系统表的事务中;最终结果是分配的页面数量越大,释放所述页面所需的时间越长,时间越长。事务(在系统表上)保持打开状态,因此更有可能阻塞(在系统表上)试图在 tempdb 中创建/删除表的其他进程(特别是对于旧的 allpages== 页级锁定和表的可能性)级锁升级)。
一种用于减少系统表争用的早期方法(当时)是减少在系统表上持有锁的时间,而一种(相对)简单的方法是在删除之前释放数据/索引页桌子。
虽然
truncate table
不会释放所有数据/索引页,但它会释放除一个 8 页(数据)范围之外的所有数据;另一个“hack”是在删除表之前删除所有索引(是的,在 sysindexes 上单独的 txn,但删除表的 txn 较小)。当您考虑到(再次,很多很多年前)只有一个“tempdb”数据库时,一些应用程序大量使用了那个“ tempdb ”数据库,任何可以减少系统表争用的“黑客” 'tempdb' 是有益的;随着时间的推移,情况有所改善……多个临时数据库、系统表上的行级锁定、更好的释放方法等。
同时,
truncate table
如果留在代码中,使用不会有任何伤害。可能有一个历史和暂时的原因。在 Postgres 9.3 之前,事件触发器不存在,并且您不能“ON DROP”触发器。
但是从 Postgres 8.4 开始可以做TRIGGER
ON TRUNCATE
所以也许有些人会做一些特殊的事情TRIGGER ON TRUNCATE
来模拟ON DROP
和确保触发器的激活他们做 TRUNCATE 和 DROP 之后。对具有外键的表执行 TRUNCATE 是有意义的。但是,对于临时表,只需 DROP 就足够了
要点
truncate
是简单且不可撤销地删除表中的所有内容(基于数据存储引擎的一些技术细节可能略有不同)——跳过大量日志记录等。drop table
在进行更改时记录所有更改。因此,为了减少日志记录并减少无用的系统流失,我怀疑可能会先截断一个非常大的表,然后再删除。truncate
可能包含在事务中(这将是最安全的选择),当然,这将允许您回滚操作。