답변:
TRUNCATE
그리고 DROP
행동과 속도가 거의 동일, 그래서 일을 TRUNCATE
잘하는 것은 전에 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;
그러나 이것은 Oracle에 해당되지 않습니다 . Oracle의 실행 취소 및 다시 실행 기능으로 기록 및 보호되지만 Oracle은 모든 DDL 문의 직전 및 직후에 암시 적 커밋을 실행 하므로 사용자가 TRUNCATE
다른 DDL 문 을 롤백 할 수 없습니다 .
TRUNCATE
전체 로그와 달리 최소 로그입니다. 그게 무슨 뜻이야? 당신 TRUNCATE
에게 테이블을 말 하십시오. 삭제 된 각 행을 트랜잭션 로그에 넣지 말고 현재 존재 TRUNCATE
하는 데이터 페이지를 할당되지 않은 것으로 표시하십시오. 그것이 너무 빠른 이유입니다. TRUNCATE
따라서 로그 판독기를 사용하여 트랜잭션 로그에서 -ed 테이블 의 행을 복구 할 수 없습니다 . 할당 해제 된 데이터 페이지에 대한 참조가 있습니다.
이것을와 비교하십시오 DELETE
. 당신이 만약 DELETE
모든 행 테이블에서 당신은 여전히 이론적으로, 트랜잭션 로그에서 삭제 된 행을 발견하고 거기에서 그들을 복구 할 수 있습니다 트랜잭션을 커밋합니다. DELETE
삭제 된 모든 행을 트랜잭션 로그에 기록 하기 때문 입니다. 큰 테이블의 경우 이보다 속도가 훨씬 느려집니다 TRUNCATE
.
TRUNCATE
, DROP
최소한의 기록 작업입니다. 즉, DROP
롤백 할 수도 있습니다. 또한와 정확히 같은 방식 으로 작동합니다TRUNCATE
. 개별 행을 삭제하는 대신 DROP
적절한 데이터 페이지를 할당되지 않은 것으로 표시하고 테이블의 메타 데이터를 삭제 된 것으로 표시합니다 .때문에 TRUNCATE
와 DROP
정확히 동일한 방식으로 작동, 그들은 단지 빨리 서로 같은 실행합니다. 테이블을 호출 하기 전에 테이블을 호출 할 필요 가 없습니다 . TRUNCATE
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 ~ 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,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
확인은 "웜 캐시"에 의존하지 않는 일부 벤치 마크를 수행하여보다 현실적인 테스트가되기를 바랍니다 (Postgres를 사용하여 다른 게시 된 답변의 동일한 특성과 일치하는지 확인). :
더 큰 데이터베이스에서 postgres 9.3.4를 사용하는 벤치 마크 (RAM 캐시에 맞지 않을 정도로 충분히 큰) :
이 테스트 DB 스크립트 사용 : https://gist.github.com/rdp/8af84fbb54a430df8fc0
10M 행으로 :
truncate: 1763ms
drop: 2091ms
truncate + drop: 1763ms (truncate) + 300ms (drop) (2063ms total)
drop + recreate: 2063ms (drop) + 242ms (recreate)
100M 행으로 :
truncate: 5516ms
truncate + drop: 5592ms
drop: 5680ms (basically, the exact same ballpark)
따라서 이것에서 나는 다음을 추측합니다 : drop은 truncate + drop (최소 최신 버전의 Postgres의 경우)만큼 빠르거나 빠릅니다 (그러나 최신 버전의 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 == 페이지 수준 잠금 및 테이블에 대한 잠재적 가능성으로 인해 불쾌 함). 레벨 잠금 에스컬레이션).
시스템 테이블에서 경합을 줄이기 위해 사용 된 초기 방법은 시스템 테이블에서 잠금이 유지되는 시간을 줄이는 것이 었으며,이를 수행하는 가장 쉬운 방법은 삭제하기 전에 데이터 / 인덱스 페이지를 할당 해제하는 것이 었습니다. 탁자.
동안 truncate table
의 할당을 해제하지 않는 모든 데이터 / 인덱스 페이지를, 그것은 하나의 8 페이지 (데이터) 정도를 제외하고 모두 할당을 해제하지 않습니다; 또 다른 '해킹'은 테이블을 삭제하기 전에 모든 인덱스를 삭제하는 것입니다 (예, sysindexes에서 txn을 분리하지만 드롭 테이블의 경우 txn은 더 작습니다).
당신은 (다시, 많은, 많은 년 전) 단지 하나의 'tempdb의'데이터베이스가 있다고 생각하고, 일부 응용 프로그램은 만들었을 때 HEAVY 그 하나의 'tempdb에'데이터베이스의 사용을의 시스템 테이블에 경합을 줄일 수있는 '해킹' 'tempdb'는 유익했다. 시간이 지남에 따라 여러 임시 데이터베이스, 시스템 테이블의 행 수준 잠금, 더 나은 할당 해제 방법 등이 개선되었습니다.
그 동안 truncate table
코드에 남아 있으면를 사용해도 아무런 문제가 없습니다.
외래 키가있는 테이블에 대해서는 TRUNCATE를 수행하는 것이 좋습니다. 그러나 임시 테이블의 경우 DROP만으로 충분합니다.