私が取り組んでいるシステムには、一時テーブルを利用する多くのストアドプロシージャとSQLスクリプトがあります。これらのテーブルを使用した後、削除することをお勧めします。
私の同僚の多く(ほとんど全員が私よりもはるかに経験豊富です)が通常これを行います:
TRUNCATE TABLE #mytemp
DROP TABLE #mytemp
私は通常DROP TABLE
、スクリプトでシングルを使用します。
TRUNCATE
直前に行う理由はありますDROP
か?
私が取り組んでいるシステムには、一時テーブルを利用する多くのストアドプロシージャと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
ログイン操作なので、それができるロールバックします。トランザクションでラップするだけです。
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;
ただし、これはOracleには当てはまらないことに注意してください。OracleはすべてのDDLステートメントの直前と直後に暗黙的なコミットを発行するため、Oracleの元に戻す機能とやり直し機能、TRUNCATE
および他のDDLステートメントによって記録および保護されますが、ロールバックできません。
TRUNCATE
完全に記録されるのではなく、最小限に記録されます。どういう意味ですか?TRUNCATE
テーブルを言ってください。削除された各行をトランザクションログに入れる代わりに、TRUNCATE
それらが存在するデータページを未割り当てとしてマークするだけです。それがとても速い理由です。これがTRUNCATE
、ログリーダーを使用してトランザクションログから-edテーブルの行を回復できない理由でもあります。割り当て解除されたデータページへの参照があります。
これと比較してくださいDELETE
。あなたの場合はDELETE
、テーブル内のすべての行およびトランザクションをコミットあなたはまだ、理論的には、トランザクション・ログに削除された行を見つけて、そこからそれらを回復することができます。これDELETE
は、削除されたすべての行をトランザクションログに書き込むためです。大きなテーブルの場合、これによりの速度よりもはるかに遅くなりTRUNCATE
ます。
TRUNCATE
にDROP
、最小限のログ操作です。 つまりDROP
、ロールバックも可能です。それはまた、それがまったく同じように機能することを意味しTRUNCATE
ます。個々の行を削除する代わりにDROP
、適切なデータページを未割り当てとしてマークし、さらにテーブルのメタデータをdeletedとしてマークします。ためTRUNCATE
とDROP
仕事全く同じように、彼らは同じように速く、互いのように実行します。 テーブルをTRUNCATE
-ingする前に-ingする意味はありませんDROP
。信じられない場合は、開発インスタンスでこのデモスクリプトを実行してください。
ウォームキャッシュを備えたローカルマシンでは、次のような結果が得られます。
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
だから、134のために万人の両方の行のテーブルDROP
とTRUNCATE
効果的にまったく時間がかかりません。(コールドキャッシュ彼らは、最初の実行または2のために約2~3秒かかる。)私はまた、より高い平均期間と信じてTRUNCATE
、その後DROP
の操作は私のローカルマシン上の負荷変動に起因しているといない組み合わせは魔法のように何らかの形であるため、個々の操作よりも桁違いに悪い。結局のところ、それらはほぼ同じものです。
これらの操作のロギングオーバーヘッドについて詳しく知りたい場合は、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
最初のバージョンでは、次のようにさまざまなシステムテーブルを更新するために少しの労力を費やしていました。
rcmodified
すべてのテーブル列の更新sys.sysrscols
rcrows
中sysrowsets
pgfirst
、pgroot
、pgfirstiam
、pcused
、pcdata
、pcreserved
でsys.sysallocunits
これらのシステムテーブルの行は、次のステートメントでテーブルが削除されたときにのみ削除されます。
TRUNCATE
vs によって実行されるロギングの完全な内訳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
ログエントリとして表示されます。としての125エクステントの割り当て解除LOP_SET_BITS LCX_GAM,LCX_IAM
。これらの操作は両方とも、関連する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
OKは、「ウォームキャッシュ」に依存しないベンチマークを実行して、より現実的なテストになることを願っています(Postgresを使用して、他の投稿された回答と同じ特性に一致するかどうかを確認します) :
大規模なデータベースでpostgres 9.3.4を使用した私のベンチマーク(できればRAMキャッシュに収まらない大きさ):
このテストDBスクリプトの使用:https : //gist.github.com/rdp/8af84fbb54a430df8fc0
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
歴史的視点を追加する...
テーブルを削除するには、いくつかのシステムテーブルを更新する必要があり、通常、これらのシステムテーブルを1つのトランザクションで変更する必要があります( "transを開始、syscolumnsを削除、sysobjectsを削除、コミット")。
「ドロップテーブル」には、テーブルに関連付けられているすべてのデータ/インデックスページの割り当てを解除する必要もあります。
何年も前に...スペースの割り当て解除プロセスがトランザクションに含まれていたため、システムテーブルも更新されました。最終的には、割り当てられたページの数が多いほど、そのページの割り当て解除に時間がかかり、トランザクション(システムテーブル上)は開いたままであるため、tempdbでテーブルを作成/ドロップしようとする他のプロセス(システムテーブル上)をブロックする可能性が高くなります(特に古いallpages ==ページレベルのロックとテーブルの可能性で厄介です)レベルロックエスカレーション)。
システムテーブルの競合を減らすために使用された初期の方法(当時)は、システムテーブルでロックが保持される時間を短縮することでした。これを行う比較的簡単な方法は、削除する前にデータ/インデックスページの割り当てを解除することでしたテーブル。
一方でtruncate table
解放していないすべてのデータ/インデックスページを、それはすべてが、1つの8ページ(データ)のエクステントの割り当てを解除しません。もう1つの「ハック」は、テーブルを削除する前にすべてのインデックスを削除することです(そう、sysindexesではtxnを分離しますが、ドロップテーブルではtxnを小さくします)。
あなたはそこだけの単一の「tempdbの」データベースだった、といくつかのアプリケーションが作られた(再び、多くの、何年も前に)ことを考慮するとHEAVYにシステム・テーブルでの競合を減らすことができる任意の「ハック」は、その単一の「tempdbの」データベースの使用を「tempdb」は有益でした。時間が経つにつれて状況が改善されました...複数の一時データベース、システムテーブルの行レベルロック、より良い割り当て解除方法など
それまでの間truncate table
、コード内に残されていれば、何を使用しても問題はありません。
外部キーを持つテーブルに対してTRUNCATEを実行するのは理にかなっています。ただし、一時テーブルにはDROPで十分です