私が取り組んでいるシステムには、一時テーブルを利用する多くのストアドプロシージャとSQLスクリプトがあります。これらのテーブルを使用した後は、削除することをお勧めします。
私の同僚の多く(ほとんどすべての人が私よりもはるかに経験が豊富です)は通常これを行います。
TRUNCATE TABLE #mytemp
DROP TABLE #mytemp
通常、スクリプトでは単一のDROP TABLE
を使用します。
TRUNCATE
の直前にDROP
を行う正当な理由はありますか?
TRUNCATE
とDROP
の動作と速度はほぼ同じであるため、TRUNCATE
の直前にDROP
を実行する必要はありません。
注:SQL Serverの観点からこの回答を書き、Sybaseにも同様に当てはまると想定しました。 これは完全にそうではない 。
注:この回答を最初に投稿したとき、他にいくつかの高い評価の回答がありました-当時受け入れられていた回答を含めて、次のようないくつかの誤った主張をしました:TRUNCATE
はログに記録されませんTRUNCATE
はロールバックできません。 TRUNCATE
はDROP
より高速です。等。
このスレッドがクリーンアップされたので、その後の反論は元の質問に接しているように見えるかもしれません。これらの神話をだまそうとしている他の人のための参照としてここに残します。
このTRUNCATE-then-DROP
パターンの動機となった可能性のある、よく知られた虚偽がいくつかあります-経験豊富なDBAの間でも蔓延しています。彼らです:
TRUNCATE
はログに記録されないため、ロールバックできません。TRUNCATE
はDROP
より高速です。これらの虚偽を反省させてください。この反論はSQL Serverの観点から書いていますが、ここで言うことはすべて、Sybaseにも同様に適用できるはずです。
TRUNCATE
はログに記録された操作なので、 itcanロールバックされます 。トランザクションでラップするだけです。
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;
ただし、これは not true for 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
したがって、134million行テーブルの場合、DROP
とTRUNCATE
のどちらも、事実上まったく時間をかけません。 (コールドキャッシュでは、最初の実行に2〜3秒かかります。)また、TRUNCATE
よりもDROP
オペレーションの平均継続時間が長いのは、ローカルマシンの負荷変動とnot組み合わせはどういうわけか、魔法のように個々の操作よりも桁違いに悪いので。結局のところ、ほとんど同じものです。
これらの操作のロギングオーバーヘッドの詳細に興味がある場合は、 Martinが簡単に説明しています です。
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 |
+-----------------+---------------+-------------------------+
したがって、TRUNCATE
の最初のバージョンは、さまざまなシステムテーブルを次のように更新するために少し労力を費やすことになります
sys.sysrscols
のすべてのテーブル列のrcmodified
を更新しますrcrows
のsysrowsets
を更新pgfirst
、pgroot
、pgfirstiam
、pcused
、pcdata
、pcreserved
in sys.sysallocunits
これらのシステムテーブルの行は、テーブルが次のステートメントで削除されたときにのみ削除されます。
TRUNCATE
と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 |
+-------------------+-------------------+--------------------+------------------+-----------+---------------+-------------+------------------+-----------+---------------+-------------+
テストは、ページごとに1行の1,000行のテーブルに対して、完全復旧モデルのデータベースで実行されました。ルートインデックスページと3つの中間レベルインデックスページにより、テーブルは合計で1,004ページを消費します。
これらのページの8つは、混合エクステントの単一ページ割り当てであり、残りは125の均一エクステントに分散されています。 8つの単一ページの割り当て解除は、8つのLOP_MODIFY_ROW,LCX_IAM
ログエントリとして表示されます。 LOP_SET_BITS LCX_GAM,LCX_IAM
としての125エクステントの割り当て解除。これらの操作はどちらも、関連するPFS
ページの更新が必要であるため、結合された133 LOP_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 |
+----------------------+--------------+-------------------+-------------------+
下の完全なスクリプト
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
「ウォームキャッシュ」に依存しないいくつかのベンチマークを実行しようと思ったので、それらがより現実的なテストになることを願っています(Postgresを使用して、他の投稿された回答の同じ特性と一致するかどうかを確認します)。 :
大規模なデータベースでpostgres 9.3.4を使用した私のベンチマーク(うまくいけば、RAMキャッシュに収まらない):
このテストDBスクリプトの使用: https://Gist.github.com/rdp/8af84fbb54a430df8fc
1,000万行:
truncate: 1763ms
drop: 2091ms
truncate + drop: 1763ms (truncate) + 300ms (drop) (2063ms total)
drop + recreate: 2063ms (drop) + 242ms (recreate)
1億行:
truncate: 5516ms
truncate + drop: 5592ms
drop: 5680ms (basically, the exact same ballpark)
したがって、これから私は次のことを推測します:ドロップは、truncate + dropと同じくらい(またはそれ以上)高速です(少なくともPostgresの最新バージョンの場合)。ただし、テーブルの向きを変えて再作成する予定の場合はドロップ+再作成よりも高速なストレートトランケートを実行することに固執します(理にかなっています)。 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 ==ページレベルのロックとテーブルの可能性で厄介です)レベルのロックエスカレーション)。
システムテーブルの競合を減らすために(当時)使用された初期の方法の1つは、システムテーブルでロックが保持される時間を短縮することでした。これを行う(比較的)簡単な方法の1つは、ドロップする前にデータ/インデックスページの割り当てを解除することでした。テーブル。
truncate table
は割り当てを解除しませんallデータ/インデックスページ、1つの8ページ(データ)エクステントを除くすべての割り当てを解除します。別の「ハック」は、テーブルを削除する前にすべてのインデックスを削除することでした(そうです、sysindexesのtxnを分離してください。ただし、テーブルの削除のtxnは小さくしてください)。
あなたがそれを(もう一度、何年も前に)単一の「tempdb」データベースがあり、いくつかのアプリケーションが[〜#〜] heavy [〜#〜]その単一の ' tempdbデータベース、「tempdb」内のシステムテーブルの競合を減らすことができる「ハッキング」は有益でした。時間の経過とともに物事は改善しました...複数の一時データベース、システムテーブルの行レベルのロック、より良い割り当て解除方法など。
それまでの間、truncate table
コードに残されていても何も害はありません。