適用対象:SQL Server
Azure SQL Database
Azure SQL Managed Instance
Microsoft Fabric の SQL データベース
ヒープとはクラスター化インデックスを使用しないテーブルのことです。 ヒープとして格納されたテーブルに、1つ以上の非クラスタインデックスを作成できます。 ヒープは順序を指定せずにデータを格納します。 通常、ヒープは最初に行を挿入した順にデータを保存します。 ただし、データベース エンジンは、行を効率的に格納するために、ヒープ内でデータを移動できます。 クエリ結果ではデータの順序を予測できません。 ヒープから返される行の順序を保証するには、ORDER BY 句を使用します。 行を格納する恒久的な論理順序を指定するには、テーブルにクラスタインデックスを作成し、テーブルがヒープにならないようにします。
Note
時には、クラスタインデックスを作成するのではなく、テーブルをヒープのままにする正当な理由があります。 しかし、ヒープを効果的に使うのは高度なスキルです。 テーブルをヒープのままにしておく妥当な理由がない限り、ほとんどのテーブルには、慎重に選択されたクラスター化インデックスが必要です。
ヒープを使用するタイミング
ヒープは頻繁に切り詰めて読み込みするテーブルに最適です。 データベース エンジンは、ヒープ内の最も早い空きスペースを埋めることで空間を最適化します。
次の点について検討してください。
- ヒープ内の空き領域を見つけることはコストがかかることがあり、特に多くの削除や更新が行われる場合にその価値があります。
- クラスタインデックスは、頻繁に切り捨てないテーブルに対して安定したパフォーマンスを提供します。
定期的に切り詰めたり再作成したりするテーブル、例えば一時テーブルやステージングテーブルでは、ヒープを使う方が効率的です。
ヒープとクラスター化インデックスのどちらを使用できるかを選択すると、データベースのパフォーマンスと効率に大きな影響を与える可能性があります。
テーブルをヒープとして格納する際、ファイル番号、データページ番号、ページ上のスロット(FileID:PageID:SlotID)からなる8バイトの行識別子(RID)を参照して個々の行を識別します。 行 ID は、小さい効率的な構造です。
ヒープを大規模で順序のない挿入操作のステージングテーブルとして使用します。 ヒープは厳密な挿入順序を強制しないため、挿入操作は通常、クラスタ化されたインデックスへの同等の挿入よりも高速です。 ヒープのデータを読み込み処理して最終目的地にするなら、クエリが使用する検索述語をカバーする狭い非クラスタ化インデックスを作成することを検討してください。
Note
ヒープからデータページを取得できる順序はありますが、挿入順とは限りません。
また、常に非クラスタインデックス経由でデータにアクセスし、RIDがクラスタインデックスキーより小さい場合、ヒープを使うこともできます。
もしテーブルがヒープで非クラスタのインデックスがなければ、どの行も見つけるためにテーブル全体(テーブルスキャン)を読み込む必要があります。 SQL Serverはヒープ上で直接RIDを探すことはできません。 テーブルが小さい場合は、この動作で対応できます。
ヒープを使用しない場合
データが頻繁にソートされた順序で返される場合は、ヒープは使わないでください。 ソート列にクラスタインデックスを置くことでソート操作を回避できます。
データが頻繁にまとめられている場合は、ヒープは使わないでください。 データはグループ化される前にソートされなければならず、ソート列にクラスタインデックスを置くことでソート操作を回避できます。
テーブルから頻繁にデータ範囲を問い合わせる場合は、ヒープは使わないでください。 範囲列にクラスター化インデックスが存在すると、ヒープ全体の並べ替えが実行されなくなります。
クラスタでないインデックスがなく、テーブルが大きい場合はヒープは使わないでください。 この設計の唯一の用途は、指定された順序なしにテーブル全体の内容を返すことです。 ヒープでは、データベース エンジンはすべての行を読み取って任意の行を検索します。
頻繁にデータを更新する場合はヒープは使わないでください。 レコードを更新して、その更新が現在使用している容量よりもデータページのスペースを多く使う場合、そのレコードは十分な空きスペースがあるデータページに移動します。 この移動により、データの新しい場所を指す 転送レコード が作成されます。 転送ポインタは、以前データが保持されていたページに書き込まれ、新しい物理的位置を示します。 この動きはヒープに断片化をもたらします。 データベース エンジンはヒープをスキャンするときに、これらのポインターに従います。 この操作は先読み性能を制限し、追加のI/Oが発生してスキャン性能を低下させることがあります。
ヒープの管理
ヒープを作成するには、クラスター化インデックスのないテーブルを作成します。 既にテーブルにクラスター化インデックスが含まれている場合は、クラスター化インデックスを削除して、テーブルをヒープに戻します。
ヒープを削除するには、ヒープにクラスター化インデックスを作成します。
ヒープを再構築して無駄な領域を解放するには、次の操作を行います。
- ヒープにクラスター化インデックスを作成し、そのクラスター化インデックスを削除します。
-
ALTER TABLE ... REBUILDコマンドを使用して、ヒープを再構築します。
Warning
クラスター化インデックスを作成または削除するには、テーブル全体を再作成する必要があります。 テーブルに非クラスタインデックスがある場合は、クラスタインデックスを変更するたびにすべての非クラスタインデックスを再作成しなければなりません。 したがって、ヒープからクラスタインデックス構造への変更、またはその逆に戻すには多くの時間がかかり、 tempdbのデータの並び替えにはディスク容量が必要になります。
ヒープを識別する
次のクエリは、現在のデータベースからヒープの一覧を取得します。 この一覧には以下が含まれます。
- テーブル名
- スキーマ名
- 行数
- テーブル サイズ (KB)
- インデックス サイズ (KB)
- 未使用の領域
- ヒープを識別する列
SELECT t.name AS 'Your TableName',
s.name AS 'Your SchemaName',
p.rows AS 'Number of Rows in Your Table',
SUM(a.total_pages) * 8 AS 'Total Space of Your Table (KB)',
SUM(a.used_pages) * 8 AS 'Used Space of Your Table (KB)',
(SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS 'Unused Space of Your Table (KB)',
CASE
WHEN i.index_id = 0 THEN 'Yes'
ELSE 'No'
END AS 'Is Your Table a Heap?'
FROM sys.tables AS t
INNER JOIN sys.indexes AS i
ON t.object_id = i.object_id
INNER JOIN sys.partitions AS p
ON i.object_id = p.object_id
AND i.index_id = p.index_id
INNER JOIN sys.allocation_units AS a
ON p.partition_id = a.container_id
LEFT OUTER JOIN sys.schemas AS s
ON t.schema_id = s.schema_id
WHERE i.index_id <= 1 -- 0 for Heap, 1 for Clustered Index
GROUP BY t.name, s.name, i.index_id, p.rows
ORDER BY 'Your TableName';
ヒープの構造
ヒープとはクラスター化インデックスを使用しないテーブルのことです。 ヒープは、 sys.partitionsに 1 行を持っており、ヒープに使われる各パーティションは index_id = 0 になります。 既定では、ヒープのパーティションは 1 つです。 ヒープにパーティションが複数ある場合、各パーティションは、そのパーティションのデータを保持するヒープ構造になります。 たとえば、ヒープに 4 つのパーティションがある場合、4 つのヒープ構造があり、それぞれのパーティションに 1 つずつ存在します。
ヒープ内のデータ型に応じて、各ヒープ構造には特定のパーティションのデータを保存・管理するための1つ以上の割り当てユニットがあります。 最低でも、各ヒープはパーティションごとに1つの IN_ROW_DATA 割り当てユニットを持ちます。 ヒープ構造には、大きなオブジェクト(LOB)列が含まれている場合、各パーティションごとに1つの LOB_DATA 割り当てユニットがあります。 また、8,060バイトの行サイズ制限を超える可変長の列を含む場合、パーティションごとに1つの ROW_OVERFLOW_DATA 割り当てユニットがあります。
sys.system_internals_allocation_unitsシステムビューの列first_iam_page、特定のパーティション内のヒープに割り当てられた領域を管理するIAMページのチェーンで最初のインデックス割り当てマップ(IAM)ページを指しています。 SQL Server は、IAM ページを使用してヒープ内を移動します。 データページとその中の行は特定の順序ではなく、リンクもされていません。 データ ページ間の論理接続は、IAM ページ内に記録されている情報だけです。
Important
sys.system_internals_allocation_unitsシステムビューは内部使用専用です。 将来の互換性は保証されません。
IAMページをスキャンしてヒープのページを保持するエクステントを見つけることで、テーブルスキャンやシリアルリードを行うことができます。 IAMはデータファイル内の拡張と同じ順序でエクステントを表現するため、この構造によりシリアルヒープスキャンは各ファイルを順番に進行させます。 IAMページを使ってスキャンシーケンスを設定することで、ヒープからの行が挿入された順に返されることは通常なくなります。
次の図は、SQL Server データベース エンジンが IAM ページを使用して、単一パーティション ヒープ内のデータ行を取得する方法を示しています。