読み取り専用レポートのためにSQL Server 2016に定期的にロードする必要のある大きなテーブル(幅が約1000万行)がたくさんあります。これらのテーブルをディスク上で可能な限り小さくしたいと思います。これは、ロードまたはクエリのパフォーマンスの向上以上に重要です。
これは、これ以上インデックスを作成する必要がないテーブルに対して私が行っていることです。
DATA_COMPRESSION=PAGE
を使用してテーブルを作成します。テーブルの列タイプは、varchar(512以下、最大ではない)、float、tinyint、またはdate(datetimeではない)です。すべての列はnull可能として作成され、主キーまたは外部キーは定義されていません。これらはクエリに関係なく、テーブルが直接更新されることはありません。すべてのデフォルトの照合はSQL_Latin1_General_CP1_CI_AS
です。
これを行うと、ページデータ圧縮がヒープに適用されたことをsys.allocation_units
で確認でき、sys.partitions
でFILL FACTORが正しく0(100%)であることを確認できます。テーブルは非圧縮テーブルよりもはるかに小さいので、圧縮は完了したと思いました。
ただし、同じオプションDATA_COMPRESSION=PAGE
を使用して再構築すると、圧縮済みのテーブルが約30%小さくなります。データページあたり約17行からページあたり25行になっているようです。 (ただし、1回だけです。その後再構築しても、最初の再構築よりも小さくはなりません。)
質問
だから私の質問は:(a)ここで何が起こっているのですか? (b)データが読み込まれた後に再構築する必要なしにテーブルを読み込むときに、この非常に小さな圧縮サイズを直接取得する方法はありますか?
@ HandyD は完全に正しいです。ヒープに挿入するときに圧縮を取得するために、他のいくつかの方法を強調したいだけです。
同じドキュメントから
ヒープがページレベルの圧縮用に構成されている場合、ページは次の方法でのみページレベルの圧縮を受け取ります。
- データは一括最適化を有効にして一括インポートされます。
- データはINSERT INTO ... WITH(TABLOCK)構文を使用して挿入され、テーブルには非クラスター化インデックスがありません。
- PAGE圧縮オプションを指定してALTER TABLE ... REBUILDステートメントを実行すると、テーブルが再構築されます。
これによると、最小限に記録された一括挿入を活用するか、INSERT INTO ... WITH (TABLOCK)
を使用して、再構築せずにPAGE
圧縮を取得できます。
(a)ここで何が起こっているのですか? (b)データが読み込まれた後に再構築する必要なしにテーブルを読み込むときに、この非常に小さな圧縮サイズを直接取得する方法はありますか?
ヒープに挿入するときにPAGE
圧縮を取得するルールがあります。圧縮を取得するには、bcp
コマンドに-h "TABLOCK"
を追加します。
ROW
圧縮はこれらの前提条件なしで機能し、以下の例で使用される最小の圧縮量です。これを指摘してくれてありがとう @ DenisRubashkin !
開始データとBCP出力コマンドの例
--Tested on SQL Server 2014 SP2
CREATE TABLE dbo.CompressedHeap_Source( Val varchar(512),
Datefield Date,
Tinyfield TinyINT,
Floatfield float)
WITH (DATA_COMPRESSION = PAGE);
INSERT INTO dbo.CompressedHeap_Source
(
Val,Datefield,Tinyfield,Floatfield)
SELECT 'Bla',cast(getdate() as date),1,1.2412
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2;
--bcp TEST.dbo.CompressedHeap_Source out E:\Data\HeapData.bcp -c -T
ROW
圧縮および非圧縮サイズ
データサイズは132272 KB
です。これは、ヒープへの標準的な挿入を行う場合、ROW
圧縮されますが、PAGE
圧縮されません。
このテストでは、圧縮しない場合のデータサイズは〜176216 KB
です。
exec sp_spaceused 'dbo.CompressedHeap_Source'
name rows reserved data index_size unused
CompressedHeap_Source 6365530 132296 KB 132272 KB 8 KB 16 KB
INSERT INTO ... WITH TABLOCK
WITH TABLOCK
を挿入すると、PAGE
圧縮データサイズ69480 KB
が得られます。
INSERT INTO dbo.CompressedHeap_Source2 WITH(TABLOCK)
(
Val,Datefield,Tinyfield,Floatfield)
SELECT 'Bla',cast(getdate() as date),1,1.2412
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2
一括挿入
ここでも、page
圧縮された宛先ヒープテーブルを作成し、一括挿入with tablock
を実行します。
CREATE TABLE dbo.CompressedHeap_Destination( Val varchar(512),
Datefield Date,
Tinyfield TinyINT,
Floatfield float)
WITH (DATA_COMPRESSION = PAGE);
bulk insert dbo.CompressedHeap_Destination
from 'E:\Data\HeapData.bcp' with (TABLOCK)
データはpage
圧縮され、69480 KB
にもあります。
name rows reserved data index_size unused
CompressedHeap_Destination 6365530 69512 KB 69480 KB 8 KB 24 KB
BCP IN TABLOCK
BULK INSERT WITH TABLOCK
ヒントとBCP IN
を使用すると、-h "TABLOCK"
と同じ結果を得ることができます。 これは理にかなっています、彼らは内部的に同じことをします
--bcp TEST.dbo.CompressedHeap_Destination2 IN E:\Data\HeapData.bcp -c -T -h "TABLOCK"
結果のサイズは69480 KB
BLOCK IN WITH TABLOCK
BCPを使用して、宛先テーブルのコピー内の同じファイルからデータをロードする
そして標準のbcpコマンドは非圧縮データになります:
--bcp TEST.dbo.CompressedHeap_Destination2 IN E:\Data\HeapData.bcp -c -T
132272 KB
のデータサイズ(行圧縮)。
圧縮に関する Docs 記事によると:
DML操作の一部としてヒープに割り当てられた新しいページは、ヒープが再構築されるまでPAGE圧縮を使用しません。圧縮を削除して再適用するか、クラスター化インデックスを作成して削除して、ヒープを再構築します。
これはあなたが見ているものと一致しているようです。再構築するまでは、実際にはテーブルが圧縮されていないようです。圧縮されていないテーブルにデータを読み込んでみて、ページあたり平均17行かどうか、またはこれが減少するかどうかを確認できます。それが変わらない場合は、圧縮されておらず、再構築が必要です。
クラスタ化インデックスをテーブルに追加することもできます。これにより、データの一括読み込み後にテーブルが圧縮解除/低圧縮されるのを防ぐことができます。