use*_*723 105 sql-server sybase truncate
在我工作的系统中有很多使用临时表的存储过程和 SQL 脚本。使用这些表后,最好删除它们。
我的许多同事(几乎所有人都比我更有经验)通常会这样做:
TRUNCATE TABLE #mytemp
DROP TABLE #mytemp
Run Code Online (Sandbox Code Playgroud)
我通常DROP TABLE在我的脚本中使用单个。
在 aTRUNCATE之前立即执行 a 有什么好的理由DROP吗?
Nic*_*mas 140
TRUNCATE并且DROP在行为和速度上几乎相同,所以在 aTRUNCATE之前做一个权利DROP是完全没有必要的。
注意:我从 SQL Server 的角度写了这个答案,并假设它同样适用于 Sybase。看来情况并非完全如此。
注意:当我第一次发布这个答案时,还有其他几个评价很高的答案——包括当时被接受的答案——做出了几个错误的声明,比如:TRUNCATE没有记录;TRUNCATE不能回滚;TRUNCATE快于DROP; 等等。
既然这条线索已经清理干净,接下来的反驳似乎与原始问题无关。我将它们留在这里作为其他人希望揭穿这些神话的参考。
有几个流行的谎言——即使在有经验的 DBA 中也很普遍——可能激发了这种TRUNCATE-then-DROP模式。他们是:
TRUNCATE未记录,因此无法回滚。TRUNCATE比DROP.让我反驳这些谎言。我是从 SQL Server 的角度写这篇反驳的,但我在这里所说的一切都应该同样适用于 Sybase。
TRUNCATE是一个记录的操作,因此它可以被回滚。只需将其包装在交易中即可。
USE [tempdb];
SET NOCOUNT ON;
CREATE TABLE truncate_demo (
whatever VARCHAR(10)
);
INSERT INTO truncate_demo (whatever)
VALUES ('log this');
BEGIN TRANSACTION;
TRUNCATE TABLE truncate_demo;
ROLLBACK TRANSACTION;
SELECT *
FROM truncate_demo;
DROP TABLE truncate_demo;
Run Code Online (Sandbox Code Playgroud)
但是请注意,这不适用于 Oracle。尽管受到 Oracle 的撤消和重做功能的记录和保护,但用户无法回滚TRUNCATE其他 DDL 语句,因为 Oracle在所有 DDL 语句之前和之后立即发出隐式提交。
TRUNCATE最少记录,而不是完全记录。这意味着什么?说你TRUNCATE一张桌子。无需将每个已删除的行都放入事务日志中,TRUNCATE只需将它们所在的数据页标记为未分配。这就是为什么它这么快。这也是您无法TRUNCATE使用日志阅读器从事务日志中恢复-ed 表的行的原因。所有你会发现有对释放的数据页的引用。
将此与DELETE. 如果您DELETE将表中的所有行都提交并提交事务,理论上您仍然可以在事务日志中找到已删除的行并从那里恢复它们。这是因为DELETE将每个删除的行写入事务日志。对于大表,这将使它比TRUNCATE.
TRUNCATE,DROP是一个最少记录的操作。 这意味着DROP也可以回滚。这也意味着它的工作方式与TRUNCATE. 不是删除单个行,而是DROP将适当的数据页标记为未分配,并将表的元数据标记为已删除。由于TRUNCATE和DROP工作方式完全相同,他们跑一样快,因为彼此。 在TRUNCATE-ing 之前DROP-ing一个表是没有意义的。如果您不相信我,请在您的开发实例上运行此演示脚本。
在我的带有热缓存的本地机器上,我得到的结果如下:
table row count: 134,217,728
run# transaction duration (ms)
TRUNCATE TRUNCATE then DROP DROP
==========================================
01 0 1 4
02 0 39 1
03 0 1 1
04 0 2 1
05 0 1 1
06 0 25 1
07 0 1 1
08 0 1 1
09 0 1 1
10 0 12 1
------------------------------------------
avg 0 8.4 1.3
Run Code Online (Sandbox Code Playgroud)
因此,对于一个 1.34亿行的表DROP,TRUNCATE实际上根本不需要时间。(在冷缓存上,第一次或两次运行大约需要 2-3 秒。)我还认为TRUNCATEthenDROP操作的平均持续时间较长可归因于我本地机器上的负载变化,而不是因为组合在某种程度上神奇地比单个操作差一个数量级。毕竟,它们几乎完全相同。
如果您对这些操作的日志记录开销的更多细节感兴趣,Martin 对此有一个简单的解释。
Mar*_*ith 53
TRUNCATE然后测试DROP与直接执行相比,DROP表明第一种方法实际上略微增加了日志记录开销,因此甚至可能会适得其反。
查看单个日志记录显示TRUNCATE ... DROP版本几乎与DROP版本相同,只是有这些附加条目。
+-----------------+---------------+-------------------------+
| Operation | Context | AllocUnitName |
+-----------------+---------------+-------------------------+
| LOP_COUNT_DELTA | LCX_CLUSTERED | sys.sysallocunits.clust |
| LOP_COUNT_DELTA | LCX_CLUSTERED | sys.sysrowsets.clust |
| LOP_COUNT_DELTA | LCX_CLUSTERED | sys.sysrscols.clst |
| LOP_COUNT_DELTA | LCX_CLUSTERED | sys.sysrscols.clst |
| LOP_HOBT_DDL | LCX_NULL | NULL |
| LOP_MODIFY_ROW | LCX_CLUSTERED | sys.sysallocunits.clust |
| LOP_HOBT_DDL | LCX_NULL | NULL |
| LOP_MODIFY_ROW | LCX_CLUSTERED | sys.sysrowsets.clust |
| LOP_LOCK_XACT | LCX_NULL | NULL |
+-----------------+---------------+-------------------------+
Run Code Online (Sandbox Code Playgroud)
所以第TRUNCATE一个版本最终浪费了一点精力对各种系统表进行了一些更新,如下所示
rcmodified中的所有表列sys.sysrscolsrcrows于sysrowsetspgfirst, pgroot, pgfirstiam, pcused, pcdata,pcreserved输入sys.sysallocunits只有在下一条语句中删除表时,这些系统表行才会最终被删除。
TRUNCATEvs执行的日志记录的完整分解DROP如下。我还添加DELETE了用于比较目的。
+-------------------+-------------------+--------------------+------------------+-----------+---------------+-------------+------------------+-----------+---------------+-------------+
| | | | Bytes | Count |
+-------------------+-------------------+--------------------+------------------+-----------+---------------+-------------+------------------+-----------+---------------+-------------+
| Operation | Context | AllocUnitName | Truncate / Drop | Drop Only | Truncate Only | Delete Only | Truncate / Drop | Drop Only | Truncate Only | Delete Only |
+-------------------+-------------------+--------------------+------------------+-----------+---------------+-------------+------------------+-----------+---------------+-------------+
| LOP_BEGIN_XACT | LCX_NULL | | 132 | 132 | 132 | 132 | 1 | 1 | 1 | 1 |
| LOP_COMMIT_XACT | LCX_NULL | | 52 | 52 | 52 | 52 | 1 | 1 | 1 | 1 |
| LOP_COUNT_DELTA | LCX_CLUSTERED | System Table | 832 | | 832 | | 4 | | 4 | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | System Table | 2864 | 2864 | | | 22 | 22 | | |
| LOP_DELETE_ROWS | LCX_MARK_AS_GHOST | T | | | | 8108000 | | | | 1000 |
| LOP_HOBT_DDL | LCX_NULL | | 108 | 36 | 72 | | 3 | 1 | 2 | |
| LOP_LOCK_XACT | LCX_NULL | | 336 | 296 | 40 | | 8 | 7 | 1 | |
| LOP_MODIFY_HEADER | LCX_PFS | Unknown Alloc Unit | 76 | 76 | | 76 | 1 | 1 | | 1 |
| LOP_MODIFY_ROW | LCX_CLUSTERED | System Table | 644 | 348 | 296 | | 5 | 3 | 2 | |
| LOP_MODIFY_ROW | LCX_IAM | T | 800 | 800 | 800 | | 8 | 8 | 8 | |
| LOP_MODIFY_ROW | LCX_PFS | T | 11736 | 11736 | 11736 | | 133 | 133 | 133 | |
| LOP_MODIFY_ROW | LCX_PFS | Unknown Alloc Unit | 92 | 92 | 92 | | 1 | 1 | 1 | |
| LOP_SET_BITS | LCX_GAM | T | 9000 | 9000 | 9000 | | 125 | 125 | 125 | |
| LOP_SET_BITS | LCX_IAM | T | 9000 | 9000 | 9000 | | 125 | 125 | 125 | |
| LOP_SET_BITS | LCX_PFS | System Table | 896 | 896 | | | 16 | 16 | | |
| LOP_SET_BITS | LCX_PFS | T | | | | 56000 | | | | 1000 |
| LOP_SET_BITS | LCX_SGAM | Unknown Alloc Unit | 168 | 224 | 168 | | 3 | 4 | 3 | |
+-------------------+-------------------+--------------------+------------------+-----------+---------------+-------------+------------------+-----------+---------------+-------------+
| Total | | | 36736 | 35552 | 32220 | 8164260 | 456 | 448 | 406 | 2003 |
+-------------------+-------------------+--------------------+------------------+-----------+---------------+-------------+------------------+-----------+---------------+-------------+
Run Code Online (Sandbox Code Playgroud)
该测试是在具有完整恢复模型的数据库中针对每页一行的 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日志条目(如下所示)
+----------------------+--------------+-------------------+-------------------+
| Object | Rows Deleted | Number of Indexes | Delete Operations |
+----------------------+--------------+-------------------+-------------------+
| sys.sysallocunits | 1 | 2 | 2 |
| sys.syscolpars | 2 | 2 | 4 |
| sys.sysidxstats | 1 | 2 | 2 |
| sys.sysiscols | 1 | 2 | 2 |
| sys.sysobjvalues | 1 | 1 | 1 |
| sys.sysrowsets | 1 | 1 | 1 |
| sys.sysrscols | 2 | 1 | 2 |
| sys.sysschobjs | 2 | 4 | 8 |
+----------------------+--------------+-------------------+-------------------+
| | | | 22 |
+----------------------+--------------+-------------------+-------------------+
Run Code Online (Sandbox Code Playgroud)
完整脚本如下
DECLARE @Results TABLE
(
Testing int NOT NULL,
Operation nvarchar(31) NOT NULL,
Context nvarchar(31) NULL,
AllocUnitName nvarchar(1000) NULL,
SumLen int NULL,
Cnt int NULL
)
DECLARE @I INT = 1
WHILE @I <= 4
BEGIN
IF OBJECT_ID('T','U') IS NULL
CREATE TABLE T(N INT PRIMARY KEY,Filler char(8000) NULL)
INSERT INTO T(N)
SELECT DISTINCT TOP 1000 number
FROM master..spt_values
CHECKPOINT
DECLARE @allocation_unit_id BIGINT
SELECT @allocation_unit_id = allocation_unit_id
FROM sys.partitions AS p
INNER JOIN sys.allocation_units AS a
ON p.hobt_id = a.container_id
WHERE p.object_id = object_id('T')
DECLARE @LSN NVARCHAR(25)
DECLARE @LSN_HEX NVARCHAR(25)
SELECT @LSN = MAX([Current LSN])
FROM fn_dblog(null, null)
SELECT @LSN_HEX=
CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 1, 8),2) AS INT) AS VARCHAR) + ':' +
CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 10, 8),2) AS INT) AS VARCHAR) + ':' +
CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 19, 4),2) AS INT) AS VARCHAR)
BEGIN TRAN
IF @I = 1
BEGIN
TRUNCATE TABLE T
DROP TABLE T
END
ELSE
IF @I = 2
BEGIN
DROP TABLE T
END
ELSE
IF @I = 3
BEGIN
TRUNCATE TABLE T
END
ELSE
IF @I = 4
BEGIN
DELETE FROM T
END
COMMIT
INSERT INTO @Results
SELECT @I,
CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END,
Context,
CASE
WHEN AllocUnitId = @allocation_unit_id THEN 'T'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Table'
ELSE AllocUnitName
END,
COALESCE(SUM([Log Record Length]), 0) AS [Size in Bytes],
COUNT(*) AS Cnt
FROM fn_dblog(@LSN_HEX, null) AS D
WHERE [Current LSN] > @LSN
GROUP BY GROUPING SETS((Operation, Context,
CASE
WHEN AllocUnitId = @allocation_unit_id THEN 'T'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Table'
ELSE AllocUnitName
END),())
SET @I+=1
END
SELECT Operation,
Context,
AllocUnitName,
AVG(CASE WHEN Testing = 1 THEN SumLen END) AS [Truncate / Drop Bytes],
AVG(CASE WHEN Testing = 2 THEN SumLen END) AS [Drop Bytes],
AVG(CASE WHEN Testing = 3 THEN SumLen END) AS [Truncate Bytes],
AVG(CASE WHEN Testing = 4 THEN SumLen END) AS [Delete Bytes],
AVG(CASE WHEN Testing = 1 THEN Cnt END) AS [Truncate / Drop Count],
AVG(CASE WHEN Testing = 2 THEN Cnt END) AS [Drop Count],
AVG(CASE WHEN Testing = 3 THEN Cnt END) AS [Truncate Count],
AVG(CASE WHEN Testing = 4 THEN Cnt END) AS [Delete Count]
FROM @Results
GROUP BY Operation,
Context,
AllocUnitName
ORDER BY Operation, Context,AllocUnitName
DROP TABLE T
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
38656 次 |
| 最近记录: |