次の方法で共有


ヒープ (クラスター化インデックスなしのテーブル)

適用対象: SQL Server Azure SQL データベース Azure SQL Managed Instance

ヒープとはクラスター化インデックスを使用しないテーブルのことです。 1 つまたは複数の非クラスター化インデックスを、ヒープとして格納されているテーブルに作成することができます。 ヒープには、順序を指定せずにデータが格納されます。 通常、データは最初は行が挿入される順序で格納されます。 ただし、データベース エンジンは、行を効率的に格納するために、ヒープ内でデータを移動できます。 クエリ結果では、データの順序を予測できません。 ヒープから返される行の順序を保証するには、ORDER BY 句を使用します。 行を格納するための永久的な論理的順序を指定するには、テーブルにクラスター化インデックスを作成し、テーブルがヒープにならないようにします。

Note

クラスター化インデックスを作成する代わりにテーブルをヒープのままにしておくとよい場合もありますが、ヒープを効果的に使用するには高度なスキルが必要です。 テーブルをヒープのままにしておく妥当な理由がない限り、ほとんどのテーブルには、慎重に選択されたクラスター化インデックスが必要です。

ヒープを使用するべきタイミング

ヒープは、頻繁に切り捨てられ、再読み込みされるテーブルに最適です。 データベース エンジンは、使用可能な最も早い領域を埋めることで、ヒープ内の領域を最適化します。

次の点について検討してください。

  • ヒープ内の空き領域を見つけると、特に削除や更新が多い場合にコストがかかる場合があります。
  • クラスター化インデックスは、頻繁に切り捨てられないテーブルに対して安定したパフォーマンスを提供します。

一時テーブルやステージング テーブルなど、定期的に切り捨てられたり再作成されたりするテーブルの場合は、多くの場合、ヒープを使用する方が効率的です。

ヒープとクラスター化インデックスのどちらを使用できるかを選択すると、データベースのパフォーマンスと効率に大きな影響を与える可能性があります。

テーブルをヒープとして格納する場合、個々の行は、ファイル番号、データ ページ番号、ページのスロット (FileID:PageID:SlotID) で構成される 8 バイトの行識別子 (RID) への参照で識別されます。 行 ID は、小さい効率的な構造です。

ヒープは、順序指定されていな大規模な挿入操作のステージング テーブルとして使用できます。 厳密な順序を適用せずにデータが挿入されるため、通常この挿入操作は、クラスター化インデックスへの同等の挿入より高速です。 ヒープのデータが読み取られて最終的な宛先へ処理される場合は、クエリで使用される検索述語を対象とする、狭い非クラスター化インデックスを作成すると便利な場合があります。

Note

データはデータページの順にヒープから取得されますが、必ずしもデータが挿入された順序ではありません。

データが常に非クラスター化インデックスを介してアクセスされ、RID がクラスター化インデックス キーより小さい場合も、データ担当者はヒープを使用することがあります。

テーブルがヒープで、非クラスター化インデックスがない場合、行を検索するには、テーブル全体を読み取る (テーブル スキャンする) 必要があります。 SQL Server は、ヒープで直接 RID をシークできません。 テーブルが小さい場合は、この動作で対応できます。

ヒープを使用すべきではないタイミング

データが、並べ替えた順序で頻繁に取得される場合は、ヒープを使用しないでください。 並べ替え用の列にクラスター化インデックスが存在すると、並べ替え操作が実行されない場合があります。

データが頻繁にグループ化される場合は、ヒープを使用しないでください。 データの並べ替えはグループ化よりも前に行う必要がありますが、並べ替え用の列にクラスター化インデックスが存在すると、並べ替え操作が実行されない場合があります。

広範囲のデータがテーブルから頻繁に照会される場合は、ヒープを使用しないでください。 範囲列にクラスター化インデックスが存在すると、ヒープ全体の並べ替えが実行されなくなります。

非クラスター化インデックスが存在せず、テーブルが大きい場合は、ヒープを使用しないでください。 この設計の唯一のアプリケーションは、指定された順序なしでテーブルの内容全体を返します。 ヒープでは、データベース エンジンはすべての行を読み取って任意の行を検索します。

データが頻繁に更新される場合は、ヒープを使用しないでください。 レコードを更新し、その更新によって、使用されるデータ ページ内の領域が現在よりも増える場合、十分な空き領域があるデータ ページにレコードを移動する必要があります。 これにより、データの新しい場所を指す転送レコードが作成され、以前にそのデータを保持していたページに、新しい物理的な場所を示す転送ポインターを書き込む必要があります。 これは、ヒープの断片化を招きます。 データベース エンジンはヒープをスキャンするときに、これらのポインターに従います。 このアクションにより、先読みのパフォーマンスが制限され、追加の I/O が発生してスキャンのパフォーマンスが低下する可能性があります。

マネージド ヒープ

ヒープを作成するには、クラスター化インデックスのないテーブルを作成します。 既にテーブルにクラスター化インデックスが含まれている場合は、クラスター化インデックスを削除して、テーブルをヒープに戻します。

ヒープを削除するには、ヒープにクラスター化インデックスを作成します。

ヒープを再構築して無駄な領域を解放するには、次の操作を行います。

  • ヒープにクラスター化インデックスを作成し、そのクラスター化インデックスを削除します。
  • ALTER TABLE ... REBUILD コマンドを使用して、ヒープを再構築します。

警告

クラスター化インデックスを作成または削除するには、テーブル全体を再作成する必要があります。 テーブルに非クラスター化インデックスがある場合は、クラスター化インデックスが変更されるたびに、すべての非クラスター化インデックスを再作成する必要があります。 このため、ヒープからクラスター化インデックス構造への変更、またはその逆の変更には時間がかかり、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 t
INNER JOIN sys.indexes i
    ON t.object_id = i.object_id
INNER JOIN sys.partitions p
    ON i.object_id = p.object_id
        AND i.index_id = p.index_id
INNER JOIN sys.allocation_units a
    ON p.partition_id = a.container_id
LEFT JOIN sys.schemas 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 アロケーション ユニットがあります。

first_iam_page システム ビュー内の列 sys.system_internals_allocation_units は、特定のパーティション内のヒープに割り当てられた領域を管理する IAM ページのチェーン内の最初の IAM ページをポイントします。 SQL Server は、IAM ページを使用してヒープ内を移動します。 データ ページとその中にある行は特定の順序になっておらず、リンクもされていません。 データ ページ間の論理接続は、IAM ページ内に記録されている情報だけです。

重要

sys.system_internals_allocation_units システム ビューは SQL Server の内部使用専用に予約されています。 将来の互換性は保証されません。

IAM をスキャンしてヒープのページを保持しているエクステントを見つけることによって、ヒープのテーブル スキャンまたはシリアル読み取りが行われます。 IAM ではエクステントがデータ ファイルに入っている順序で表されています。したがって、シリアル ヒープ スキャンでは各ファイルが順に読み取られて進行します。 スキャン シーケンスの設定に IAM ページを使用すると、ヒープの行が挿入順どおりに戻されるとは限らないことになります。

次の図に、IAM ページを使用して単一パーティションのヒープ内のデータ行が SQL Server データベース エンジンで取得されるしくみを示します。

iam_heap

CREATE INDEX (Transact-SQL)
DROP INDEX (Transact-SQL)
クラスター化インデックスと非クラスター化インデックスの概念