TRUNCATE와 DROP을 모두 사용하는 이유는 무엇입니까?


100

내가 작업하는 시스템에는 임시 테이블을 사용하는 많은 저장 프로 시저와 SQL 스크립트가 있습니다. 이 테이블을 사용한 후에는 삭제하는 것이 좋습니다.

많은 동료들 (거의 모든 사람들이 나보다 훨씬 더 경험이 많음)은 일반적으로 다음과 같이합니다.

TRUNCATE TABLE #mytemp
DROP TABLE #mytemp

나는 일반적으로 DROP TABLE내 스크립트에서 단일 을 사용합니다 .

TRUNCATE직전 작업을 수행 할만한 이유 가 DROP있습니까?

답변:


130

아니.

TRUNCATE그리고 DROP행동과 속도가 거의 동일, 그래서 일을 TRUNCATE잘하는 것은 전에 DROP간단하게 할 필요가 없습니다.


참고 :이 답변은 SQL Server 관점에서 작성했으며 Sybase에도 동일하게 적용된다고 가정했습니다. 이것은 전적으로 그런 것은 아닙니다 .

참고 :이 답변을 처음 게시했을 때 승인 된 답변을 포함하여 몇 가지 다른 높은 등급의 답변이있었습니다 TRUNCATE. TRUNCATE롤백 할 수 없습니다. TRUNCATE보다 빠르다 DROP; 기타

이 실이 정리되었으므로, 그에 따른 반박은 원래의 질문에 접하는 것처럼 보일 수 있습니다. 나는이 신화를 폭로하고자하는 다른 사람들을위한 참고 자료로 여기에 남겨둔다.


숙련 된 DBA들 사이에서도 널리 퍼져있는 몇 가지 널리 알려진 허위가이 TRUNCATE-then-DROP패턴 을 유발했을 수 있습니다 . 그들은:

  • 신화 : TRUNCATE기록되지 않으므로 롤백 할 수 없습니다.
  • 신화 : TRUNCATE보다 빠릅니다 DROP.

이 거짓을 반박하겠습니다. SQL Server 관점 에서이 반박을 작성하고 있지만 여기서 말하는 것은 Sybase에도 동일하게 적용되어야합니다.

TRUNCATE 기록되고 롤백 될 있습니다.

  • 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.

DROP 은 TRUNCATE만큼 빠릅니다.

  • 마찬가지로 TRUNCATE, DROP최소한의 기록 작업입니다. 즉, DROP롤백 할 수도 있습니다. 또한와 정확히 같은 방식 으로 작동합니다TRUNCATE . 개별 행을 삭제하는 대신 DROP적절한 데이터 페이지를 할당되지 않은 것으로 표시하고 테이블의 메타 데이터를 삭제 된 것으로 표시합니다 .
  • 때문에 TRUNCATEDROP정확히 동일한 방식으로 작동, 그들은 단지 빨리 서로 같은 실행합니다. 테이블을 호출 하기 전에 테이블을 호출 할 필요 가 없습니다 . TRUNCATEDROP나를 믿지 않으면 개발 인스턴스 에서이 데모 스크립트 를 실행하십시오 .

    웜 캐시가있는 로컬 컴퓨터에서 얻을 수있는 결과는 다음과 같습니다.

    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에 대한 백만 행 테이블 모두 DROPTRUNCATE전혀 효율적으로 시간을하지 않습니다. (감기 캐시 그들은 처음 실행 한 두 2 ~ 3 초 정도 걸릴.) 나는 또한에 대한 높은 평균 기간 있다고 생각 TRUNCATE다음 DROP작업은 내 로컬 컴퓨터에 부하 변동에 기인하고 있지 조합이 마술 어떻게 든이기 때문에 개별 작업보다 더 큰 규모의 순서. 결국 그들은 거의 똑같습니다.

    이러한 작업의 로깅 오버 헤드에 대한 자세한 내용은 Martin이 이에 대한 간단한 설명 을 제공합니다.


52

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
  • 업데이트 rcrowssysrowsets
  • 제로 아웃 pgfirst, pgroot, pgfirstiam, pcused, pcdata, pcreservedsys.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        |
+-------------------+-------------------+--------------------+------------------+-----------+---------------+-------------+------------------+-----------+---------------+-------------+

테스트는 페이지 당 하나의 행이있는 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

2

확인은 "웜 캐시"에 의존하지 않는 일부 벤치 마크를 수행하여보다 현실적인 테스트가되기를 바랍니다 (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


1

역사적 관점 추가 ...

테이블을 삭제하려면 여러 시스템 테이블을 업데이트해야하며, 일반적으로 단일 트랜잭션에서 이러한 시스템 테이블을 변경해야합니다 ( "트랜 시작, syscolumns 삭제, sysobjects 삭제, 커밋"생각).

또한 테이블 삭제와 함께 테이블과 관련된 모든 데이터 / 인덱스 페이지를 할당 해제해야합니다.

공간 할당 취소 프로세스는 시스템 테이블도 업데이트 한 트랜잭션에 포함되었습니다. 결과적으로 할당 된 페이지 수가 많을수록 해당 페이지를 할당 해제하는 데 시간이 오래 걸리고 트랜잭션 (시스템 테이블에서)이 열려 있으므로 tempdb에서 테이블을 만들거나 삭제하려고하는 다른 프로세스 (시스템 테이블에서)를 차단할 가능성이 더 높습니다 (특히 오래된 allpages == 페이지 수준 잠금 및 테이블에 대한 잠재적 가능성으로 인해 불쾌 함). 레벨 잠금 에스컬레이션).

시스템 테이블에서 경합을 줄이기 위해 사용 된 초기 방법은 시스템 테이블에서 잠금이 유지되는 시간을 줄이는 것이 었으며,이를 수행하는 가장 쉬운 방법은 삭제하기 전에 데이터 / 인덱스 페이지를 할당 해제하는 것이 었습니다. 탁자.

동안 truncate table의 할당을 해제하지 않는 모든 데이터 / 인덱스 페이지를, 그것은 하나의 8 페이지 (데이터) 정도를 제외하고 모두 할당을 해제하지 않습니다; 또 다른 '해킹'은 테이블을 삭제하기 전에 모든 인덱스를 삭제하는 것입니다 (예, sysindexes에서 txn을 분리하지만 드롭 테이블의 경우 txn은 더 작습니다).

당신은 (다시, 많은, 많은 년 전) 단지 하나의 'tempdb의'데이터베이스가 있다고 생각하고, 일부 응용 프로그램은 만들었을 때 HEAVY 그 하나의 'tempdb에'데이터베이스의 사용을의 시스템 테이블에 경합을 줄일 수있는 '해킹' 'tempdb'는 유익했다. 시간이 지남에 따라 여러 임시 데이터베이스, 시스템 테이블의 행 수준 잠금, 더 나은 할당 해제 방법 등이 개선되었습니다.

그 동안 truncate table코드에 남아 있으면를 사용해도 아무런 문제가 없습니다.


-2

외래 키가있는 테이블에 대해서는 TRUNCATE를 수행하는 것이 좋습니다. 그러나 임시 테이블의 경우 DROP만으로 충분합니다.


TRUNCATE는 외래 키 충돌을 피할 수 있습니까? 어떻게?
user259412

1
외래 키가 있다는 오류를 기록합니다
Evgeniy Gribkov

-8

중요한 점은 truncate테이블의 모든 것을 간단하고 돌이킬 수 없게 제거하는 것입니다 (데이터 저장소 엔진을 기반으로 한 일부 기술적 사양은 약간 다를 수 있음)-무거운 로깅을 건너 뜁니다.

drop table변경 사항이 작성 될 때 모든 변경 사항을 기록합니다. 따라서 로깅을 최소화하고 쓸데없는 시스템 이탈을 줄이려면 매우 큰 테이블이 먼저 잘린 다음 떨어질 수 있습니다.

truncate 물론 가장 안전한 옵션 인 트랜잭션에 래핑되어 작업을 롤백 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.