web-dev-qa-db-ja.com

テーブルの未使用スペースに14 GBがある-テーブルサイズを縮小する方法

次のスクリプトを使用して、Azure上のSQL Serverであるデータベースからデータを収集します。

-- Script to run against database to gather metrics

CREATE TABLE #SpaceUsed (name sysname,rows bigint,reserved sysname,data sysname,index_size sysname,unused sysname)

DECLARE @Counter int 
DECLARE @Max int 
DECLARE @Table sysname

SELECT  name, IDENTITY(int,1,1) ROWID 
INTO       #TableCollection 
FROM    sysobjects 
WHERE xtype = 'U' 
ORDER BY lower(name)

SET @Counter = 1 
SET @Max = (SELECT Max(ROWID) FROM #TableCollection)

WHILE (@Counter <= @Max) 
    BEGIN 
        SET @Table = (SELECT name FROM #TableCollection WHERE ROWID = @Counter) 
        INSERT INTO #SpaceUsed 
        EXECUTE sp_spaceused @Table 
        SET @Counter = @Counter + 1 
    END

SELECT * FROM #SpaceUsed

DROP TABLE #TableCollection 
DROP TABLE #SpaceUsed

私のテーブルの1つは53 GB in size38 GB in data14 GB in unused。インデックスは11 MB in size。それを取り戻すにはどうすればよいですか14 GB that is unused?これはAzureにあるため、余分なスペースにはコストがかかります。

さまざまな記事を読んだ後、次のことを試して、テーブルのサイズを小さくしました。

DBCC DBREINDEX ('myTableName', ' ')
DBCC SHRINKDATABASE (myDatabaseName, 10);

ただし、これらが完了しても、テーブルのサイズは変更されません。未使用のスペースを再利用するために、テーブルとデータベースのサイズを縮小するにはどうすればよいですか?

更新:テーブルには24列があり、9つのフィールドがvarchar(> 4000)またはvarchar(MAX)です。別の8列は一意の識別子タイプです。

4
webworm

ヒープでもクラスタ化インデックスでも、再構築することでテーブル内のスペースを再利用できることがわかります。

ALTER INDEX ALL ON dbo.myTableName REBUILD;

テーブル内のスペースの再利用とデータベースの縮小は、2つのまったく別のものであることに注意してください。データベースを縮小することは例外的なことです。ディスク容量を解放するためだけにデータベースを縮小しないでください。ほとんどの場合、データベースはとにかく拡大してその容量を再び使用する必要があるためです。

テーブルの分散方法を決定するために、sp_spaceusedは、すべてのデータを含む単一の項目を提供するだけなので、ほとんど役に立ちません。まず、テーブルが分割されているかどうかを確認します。これは次のようにしてすばやく確認できます。

SELECT COUNT(*)
  FROM sys.partitions
  WHERE partition_number > 1
  AND object_id = OBJECT_ID(N'dbo.myTableName');

次に、7つのインデックス間でデータがどのように配置されるかを確認します。これらの1つが必要以上に大きい場合は、LOBスペースが不足している可能性があるため、ドロップ/再作成(たとえば、XML列をINCLUDEリストから削除する可能性がある)を試すことができます。削除した行またはNULLに設定した行は、再構築した後でも完全に回復されません。

 ;WITH x AS
  ( 
    SELECT i.name, type = pa.allocation_unit_type_desc, kb = COUNT(*) * 8
    FROM sys.indexes AS i
    CROSS APPLY sys.dm_db_database_page_allocations(DB_ID(), i.object_id, i.index_id, 
      NULL, 'LIMITED') AS pa
    WHERE i.[object_id] = OBJECT_ID(N'dbo.myTableName')
    GROUP BY GROUPING SETS((), (i.name, pa.allocation_unit_type_desc))
  )
  SELECT name, type, kb, 
    [%] = CONVERT(decimal(5,2), kb*100.0/(SELECT kb FROM x WHERE name IS NULL))
    FROM x
    WHERE name IS NOT NULL
    ORDER BY name, type;

ベーステーブルの個々の行のサイズを確認する場合は、次のようにします。

SELECT TOP (100) key, rowsize = DATALENGTH(col1) + DATALENGTH(col2) + ...
  FROM dbo.myTableName
  ORDER BY rowsize DESC;

かなり大きいデータがある場合は、そのデータが必要であることを確認することをお勧めします(そうでない場合は、サイズに影響している可能性があるため、NULLに設定してください)。

8
Aaron Bertrand

テーブルとインデックスのFILL FACTORを確認して、ページの多くのスペースを明示的に予約していないことを確認してください。次のコマンドを実行します。

SELECT t.name as TableName
      ,COALESCE(i.name,'<no index>') as IndexName
      ,i.type_desc
      ,i.Fill_Factor
  FROM sys.tables t INNER JOIN sys.indexes i ON t.object_id = i.object_id
;

フィルファクターは、SQLエンジンに、テーブルまたはインデックスを作成するときに、作成する各ページに残す空き容量を伝えます。ページにデータが入力されると、SQLはページがいっぱいになるまで情報をページに追加し、新しいページを作成して、適切な量(通常は約半分)のデータを新しいページに移動します。

インデックスの1つがFILL FACTORが低い場合(75未満、ただし0を超える場合(実際には0は基本的に100%にフィルされることを意味します))、データベース内の未使用スペースが大量にあることを説明している可能性があります。

インデックスのFILL FACTORを(たとえば)90%に変更したい場合は、ALTER INDEX Aaron Bertrandの回答からのコマンド:

ALTER INDEX ALL ON dbo.myTableName REBUILD WITH FILLFACTOR = 90;
3
RDFozz