web-dev-qa-db-ja.com

削除するとテーブルの未使用スペースが増えないのはなぜですか?

次の表に入力すると、

IF OBJECT_ID ('dbo.CleanTableTest', 'U') IS NOT NULL
    DROP TABLE dbo.CleanTableTest;
GO
CREATE TABLE dbo.CleanTableTest
    (FileName nvarchar(4000), 
    DocumentSummary nvarchar(max),
    Document varbinary(max)
    );
GO
-- Populate the table with data from the Production.Document table.
INSERT INTO dbo.CleanTableTest
    SELECT REPLICATE(FileName, 1000), 
           DocumentSummary, 
           Document
    FROM Production.Document;
GO

exec sp_spaceused CleanTableTest
go

結果:

name           rows  reserved  data    index_size  unused
-------------- ----  --------- ------- ----------- ------
CleanTableTest 13    456 KB    440 KB  8 KB        8 KB

しかし、すべての行を削除すると:

delete dbo.CleanTableTest
go

exec sp_spaceused CleanTableTest
go

結果:

name           rows reserved data   index_size unused
-------------- ---- -------- ------ ---------- -------
CleanTableTest 0    88 KB    80 KB  8 KB       0 KB

テーブルのすべての行の削除プロセスの後、未使用のスペースが変更されないのはなぜですか? 0 KBのままです。

6
itzik Paz

unusedがゼロである理由についての答えを見つけるには、本当にsp_spaceusedの定義を調べる必要があります。

exec sp_helptext 'sp_spaceused';
go

次のストアドプロシージャのスニペットを見てください。

begin
    /*
    ** Now calculate the summary data. 
    *  Note that LOB Data and Row-overflow Data are counted as Data Pages.
    */
    SELECT 
        @reservedpages = SUM (reserved_page_count),
        @usedpages = SUM (used_page_count),
        @pages = SUM (
            CASE
                WHEN (index_id < 2) THEN (in_row_data_page_count + lob_used_page_count + row_overflow_used_page_count)
                ELSE lob_used_page_count + row_overflow_used_page_count
            END
            ),
        @rowCount = SUM (
            CASE
                WHEN (index_id < 2) THEN row_count
                ELSE 0
            END
            )
    FROM sys.dm_db_partition_stats
    WHERE object_id = @id;

    /*
    ** Check if table has XML Indexes or Fulltext Indexes which use internal tables tied to this table
    */
    IF (SELECT count(*) FROM sys.internal_tables WHERE parent_id = @id AND internal_type IN (202,204,211,212,213,214,215,216)) > 0 
    BEGIN
        /*
        **  Now calculate the summary data. Row counts in these internal tables don't 
        **  contribute towards row count of original table.
        */
        SELECT 
            @reservedpages = @reservedpages + sum(reserved_page_count),
            @usedpages = @usedpages + sum(used_page_count)
        FROM sys.dm_db_partition_stats p, sys.internal_tables it
        WHERE it.parent_id = @id AND it.internal_type IN (202,204,211,212,213,214,215,216) AND p.object_id = it.object_id;
    END

    SELECT 
        name = OBJECT_NAME (@id),
        rows = convert (char(11), @rowCount),
        reserved = LTRIM (STR (@reservedpages * 8, 15, 0) + ' KB'),
        data = LTRIM (STR (@pages * 8, 15, 0) + ' KB'),
        index_size = LTRIM (STR ((CASE WHEN @usedpages > @pages THEN (@usedpages - @pages) ELSE 0 END) * 8, 15, 0) + ' KB'),
        unused = LTRIM (STR ((CASE WHEN @reservedpages > @usedpages THEN (@reservedpages - @usedpages) ELSE 0 END) * 8, 15, 0) + ' KB')

end

これは、sp_spaceusedの定義から直接コピーされます。 unusedが実際にどのように定義されているかに注意してください。予約済みページは使用済みページから差し引かれます(提供済み予約済みページは、使用済みページよりも合理的かつプログラム的に大きいです)。

そのため、unused列はそれです。リバースエンジニアリングがすべてを説明します。

7
Thomas Stringer

ヒープがあります。ほとんどの場合、ヒープはDELETEsでスペースを空けません。テーブルを切り捨てるか、テーブルにクラスター化インデックスを配置できます。ヒープは、挿入が多いシステムには適していますが、削除が多い場合はあまり適していません。

7
Rob Farley