次の方法で共有


sys.dm_db_index_physical_stats (Transact-SQL)

適用対象:SQL ServerAzure SQL DatabaseAzure SQL Managed Instance

SQL Server の指定したテーブルまたはビューのデータとインデックスのサイズと断片化の情報を返します。 インデックスの場合、各パーティションの B ツリーのレベルごとに 1 行のデータが返されます。 ヒープの場合、各パーティションの IN_ROW_DATA アロケーション ユニットごとに 1 行のデータが返されます。 ラージ オブジェクト (LOB) データの場合、各パーティションの割り当て単位に対して LOB_DATA 1 行が返されます。 テーブルに行オーバーフロー データが存在する場合は、各パーティションの割り当て単位に対して ROW_OVERFLOW_DATA 1 つの行が返されます。

Note

SQL Server のドキュメントでは、一般にインデックスに関して B ツリーという用語が使用されます。 行ストア インデックスで、SQL Server によって B+ ツリーが実装されます。 これは、列ストア インデックスやメモリ内データ ストアには適用されません。 詳細については、「SQL Server と Azure SQL のインデックスのアーキテクチャとデザイン ガイド」を参照してください。

sys.dm_db_index_physical_stats では、メモリ最適化インデックスに関する情報は返されません。 メモリ最適化インデックスの使用については、「sys.dm_db_xtp_index_stats (Transact-SQL)」を参照してください

可用性グループの読み取り可能なセカンダリ レプリカをホストしているサーバー インスタンスに対してクエリを実行sys.dm_db_index_physical_statsすると、ブロックのREDO問題が発生する可能性があります。 これは、この動的管理ビューは、指定されたユーザー テーブルまたはビューのロックを取得 IS し、そのユーザー テーブルまたはビューに対するロックに対するスレッドによる REDO 要求を X ブロックできるためです。

Transact-SQL 構文表記規則

構文

sys.dm_db_index_physical_stats (
    { database_id | NULL | 0 | DEFAULT }
  , { object_id | NULL | 0 | DEFAULT }
  , { index_id | NULL | 0 | -1 | DEFAULT }
  , { partition_number | NULL | 0 | DEFAULT }
  , { mode | NULL | DEFAULT }
)

引数

database_id |NULL |0 |既定

データベースの ID です。 database_idは smallint です。 有効な入力は、データベース、NULL、0、または DEFAULT の ID です。 既定値は 0 です。 NULL、0、DEFAULT は、このコンテキストで同等の値です。

SQL Server のインスタンス内のすべてのデータベースの情報を返すには、NULL を指定します。 database_idに NULL を指定する場合は、object_id、index_idpartition_numberにも NULL を指定する必要があります。

組み込み関数 DB_ID を指定できます。 データベース名を指定しないで DB_ID を使用する場合、現在のデータベースの互換性レベルが 90 以上である必要があります。

object_id |NULL |0 |既定

インデックスがオンになっているテーブルまたはビューのオブジェクト ID。 object_idは int です

有効な入力は、テーブルとビューの ID、NULL、0、または DEFAULT です。 既定値は 0 です。 NULL、0、DEFAULT は、このコンテキストで同等の値です。 SQL Server 2016 (13.x) の時点で、有効な入力には、Service Broker キュー名またはキューの内部テーブル名も含まれます。 既定のパラメーター (つまり、すべてのオブジェクト、すべてのインデックスなど) が適用されると、すべてのキューの断片化情報が結果セットに含まれます。

NULL を指定すると、指定されたデータベース内にあるすべてのテーブルとビューに関する情報が返されます。 object_idに NULL を指定する場合は、index_idとpartition_numberにも NULL を指定する必要があります。

index_id | 0 |NULL |-1 |既定

インデックスの ID です。 index_idは int です。有効な入力はインデックスの ID です。object_idがヒープ、NULL、-1、または DEFAULT の場合は 0 です。 既定値は -1 です。 NULL、-1、DEFAULT は、このコンテキストで同等の値です。

ベース テーブルまたはビューのすべてのインデックスの情報を返すには、NULL を指定します。 index_idに NULL を指定する場合は、partition_numberに NULL も指定する必要があります。

partition_number |NULL |0 |既定

オブジェクト内のパーティション番号。 partition_numberは int です。有効な入力は、インデックスまたはヒープ、NULL、0、または DEFAULT のpartion_numberです。 既定値は 0 です。 NULL、0、DEFAULT は、このコンテキストで同等の値です。

NULL を指定すると、所有するオブジェクトのすべてのパーティションに関する情報が返されます。

partition_numberは 1 から始まります。 パーティション分割されていないインデックスまたはヒープpartition_number 1 に設定されています。

mode |NULL |既定

モードの名前。 mode は、統計の取得に使用されるスキャン レベルを指定します。 modesysname です。 有効な入力値は DEFAULT、NULL、LIMITED、SAMPLED、DETAILED です。 既定値 (NULL) は LIMITED です。

返されるテーブル

列名 データ型 説明
database_id smallint テーブルまたはビューのデータベース ID。

Azure SQL Database では、値は 1 つのデータベースまたは Elastic Pool 内で一意ですが、論理サーバー内では一意ではありません。
object_id int インデックスが存在するテーブルまたはビューのオブジェクト ID。
index_id int インデックスのインデックス ID。

0 = ヒープ。
partition_number int 所有オブジェクト内の 1 から始まるパーティション番号。テーブル、ビュー、またはインデックス。

1 = パーティション分割されていないインデックスまたはヒープ。
index_type_desc nvarchar(60) インデックスの種類の説明:

- HEAP
- クラスター化インデックス
- NONCLUSTERED INDEX
- PRIMARY XML INDEX
- EXTENDED INDEX
- XML INDEX
- COLUMNSTORE MAPPING INDEX (内部)
- COLUMNSTORE DELETEBUFFER INDEX (内部)
- COLUMNSTORE DELETEBITMAP INDEX (内部)
hobt_id bigint インデックスまたはパーティションのヒープまたは B ツリー ID。

列ストア インデックスの場合、これはパーティションの内部列ストア データを追跡する行セットの ID です。 行セットは、データ ヒープまたは B ツリーとして格納されます。 親列ストア インデックスと同じインデックス ID を持ちます。 詳細については、「sys.internal_partitions (Transact-SQL)」を参照してください
alloc_unit_type_desc nvarchar(60) アロケーション ユニットの種類の説明。

-In_row_data
- LOB_DATA
- ROW_OVERFLOW_DATA

LOB_DATA割り当て単位には、text、ntext、image、varchar(max)、nvarchar(max)、varbinary(max)および xml 型の列に格納されるデータが含まれています。 詳細については、「 データ型 (Transact-SQL)」を参照してください。

ROW_OVERFLOW_DATA割り当て単位には、varchar(n)、nvarchar(n)、varbinary(n)および行外にプッシュされたsql_variantの列に格納されているデータが含まれています。
index_depth tinyint インデックス レベルの数です。

1 = ヒープまたはLOB_DATAROW_OVERFLOW_DATAアロケーション ユニット。
index_level tinyint インデックスの現在のレベル。

インデックス のリーフ レベル、ヒープ、またはROW_OVERFLOW_DATA割り当て単位のLOB_DATA場合は 0。

非リーフ インデックス レベルの場合は 0 より大きい。 index_levelは、インデックスのルート レベルで最も高くなります。

インデックスの非リーフ レベルは、mode = DETAILED の場合にのみ処理されます。
avg_fragmentation_in_percent float インデックスの論理的な断片化、または割り当てユニット内のヒープの IN_ROW_DATA エクステントの断片化。

この値はパーセンテージとして測定され、複数のファイルが考慮されます。 論理的な断片化とエクステントの断片化の定義については、「解説」を参照してください。

割り当て単位の場合 LOB_DATAROW_OVERFLOW_DATA 0。

モード = SAMPLED の場合のヒープの NULL。
fragment_count bigint アロケーション ユニットのリーフ レベル内のフラグメントの IN_ROW_DATA 数。 フラグメントの詳細については、「解説」を参照してください。

インデックスまたはROW_OVERFLOW_DATA割り当て単位の非リーフ レベルの場合は LOB_DATA NULL。

モード = SAMPLED の場合のヒープの NULL。
avg_fragment_size_in_pages float アロケーション ユニットのリーフ レベル内の 1 つのフラグメント内の IN_ROW_DATA 平均ページ数。

インデックスまたはROW_OVERFLOW_DATA割り当て単位の非リーフ レベルの場合は LOB_DATA NULL。

モード = SAMPLED の場合のヒープの NULL。
page_count bigint インデックスまたはデータ ページの合計数。

インデックスの場合、割り当て単位の B ツリーの現在のレベルのインデックス ページの IN_ROW_DATA 合計数。

ヒープの場合、アロケーション ユニット内のデータ ページの IN_ROW_DATA 合計数。

割り当て単位のROW_OVERFLOW_DATA場合LOB_DATA、割り当て単位内のページの合計数。
avg_page_space_used_in_percent float すべてのページで使用される使用可能なデータストレージ領域の平均パーセンテージ。

インデックスの場合、平均はアロケーション ユニットの B ツリーの現在のレベルに IN_ROW_DATA 適用されます。

ヒープの場合、アロケーション ユニット内のすべてのデータ ページの IN_ROW_DATA 平均。

割り当て単位のROW_OVERFLOW_DATA場合LOB_DATA、割り当て単位内のすべてのページの平均。

モード = LIMITED の場合は NULL。
record_count bigint レコードの合計数。

インデックスの場合、レコードの合計数は、アロケーション ユニットの B ツリーの現在のレベルに IN_ROW_DATA 適用されます。

ヒープの場合、アロケーション ユニット内 IN_ROW_DATA のレコードの合計数。

注: ヒープの場合、この関数から返されるレコードの数が、ヒープに対して a を実行 SELECT COUNT(*) して返される行数と一致しない可能性があります。 これは、1 行に複数のレコードが含まれる場合があるためです。 たとえば、更新の状況によっては、更新操作の結果として転送元レコードと転送先レコードが 1 つのヒープ行に含まれることがあります。 また、ほとんどの大きな LOB 行は、ストレージ内の複数のレコードに LOB_DATA 分割されます。

割り当て単位のROW_OVERFLOW_DATA場合LOB_DATA、割り当て単位全体のレコードの合計数。

モード = LIMITED の場合は NULL。
ghost_record_count bigint アロケーション ユニットで、非実体クリーンアップ タスクによる削除の準備ができているゴースト レコードの数。

割り当て単位内のインデックスの非リーフ レベルの場合は IN_ROW_DATA 0。

モード = LIMITED の場合は NULL。
version_ghost_record_count bigint 割り当て単位で未処理のスナップショット分離トランザクションによって保持されるゴースト レコードの数。

割り当て単位内のインデックスの非リーフ レベルの場合は IN_ROW_DATA 0。

モード = LIMITED の場合は NULL。
min_record_size_in_bytes int 最小レコード サイズ (バイト単位)。

インデックスの場合、最小レコード サイズは、アロケーション ユニットの B ツリーの現在のレベルに IN_ROW_DATA 適用されます。

ヒープの場合、割り当て単位の IN_ROW_DATA 最小レコード サイズ。

割り当て単位のROW_OVERFLOW_DATA場合LOB_DATA、完全な割り当て単位の最小レコード サイズ。

モード = LIMITED の場合は NULL。
max_record_size_in_bytes int 最大レコード サイズ (バイト単位)。

インデックスの場合、最大レコード サイズは、アロケーション ユニットの B ツリーの現在のレベルに IN_ROW_DATA 適用されます。

ヒープの場合、割り当て単位の IN_ROW_DATA 最大レコード サイズ。

割り当て単位のROW_OVERFLOW_DATA場合LOB_DATA、完全な割り当て単位の最大レコード サイズ。

モード = LIMITED の場合は NULL。
avg_record_size_in_bytes float 平均レコード サイズ (バイト単位)。

インデックスの場合、平均レコード サイズは、割り当て単位の B ツリーの現在のレベルに IN_ROW_DATA 適用されます。

ヒープの場合、割り当て単位の IN_ROW_DATA 平均レコード サイズ。

割り当て単位のROW_OVERFLOW_DATA場合LOB_DATAは、割り当て単位全体の平均レコード サイズ。

モード = LIMITED の場合は NULL。
forwarded_record_count bigint 別のデータの場所への転送ポインターを持つ、ヒープ内の転送されたレコード数 (この状態は、更新中に、元の場所に新しい行を格納するのに十分な空きがない場合に発生します)。

ヒープの割り当て単位以外の IN_ROW_DATA 割り当て単位の場合は NULL。

モード = LIMITED の場合、ヒープの場合は NULL。
compressed_page_count bigint 圧縮されたページの数。

ヒープの場合、新しく割り当てられたページは PAGE 圧縮されません。 ヒープは、2 つの特殊な条件、つまりデータを一括インポートする場合、またはヒープを再構築する場合に、ページ圧縮されます。 ページ割り当てを引き起こす一般的な DML 操作は、PAGE 圧縮されません。 値が目的のしきい値を超えたときに compressed_page_count ヒープを再構築します。

クラスター化インデックスを持つテーブルの場合、値は compressed_page_count PAGE 圧縮の有効性を示します。
columnstore_delete_buffer_state tinyint 0 = NOT_APPLICABLE
1 = OPEN
2 = ドレイン
3 = フラッシュ
4 = 廃止
5 = 準備完了

適用対象: SQL Server 2016 (13.x) 以降のバージョン、Azure SQL Database、および Azure SQL Managed Instance
columnstore_delete_buffer_state_desc nvarchar(60) NOT VALID - 親インデックスが列ストア インデックスではありません。

OPEN - 削除プログラムとスキャナーはこれを使用します。

ドレイン - 削除ツールはドレインされていますが、スキャナーでは引き続き使用されます。

FLUSHING - バッファーが閉じられ、バッファー内の行が削除ビットマップに書き込まれます。

RETIRING - 閉じた削除バッファー内の行は削除ビットマップに書き込まれていますが、スキャナーがまだ使用しているため、バッファーは切り捨てられません。 オープン バッファーで十分であるため、新しいスキャナーではリタイア バッファーを使用する必要はありません。

READY - この削除バッファーは使用できる状態です。

適用対象: SQL Server 2016 (13.x) 以降のバージョン、Azure SQL Database、および Azure SQL Managed Instance
version_record_count bigint これは、このインデックスに保持されている行バージョン レコードの数です。 これらの行バージョンは、高速データベース復旧機能によって管理されます。

適用対象: SQL Server 2019 (15.x) 以降のバージョンと Azure SQL Database
inrow_version_record_count bigint 高速取得のためにデータ行に保持されている ADR バージョン レコードの数。

適用対象: SQL Server 2019 (15.x) 以降のバージョンと Azure SQL Database
inrow_diff_version_record_count bigint ベース バージョンとの違いの形式で保持される ADR バージョン レコードの数。

適用対象: SQL Server 2019 (15.x) 以降のバージョンと Azure SQL Database
total_inrow_version_payload_size_in_bytes bigint このインデックスの行内バージョン レコードの合計サイズ (バイト単位)。

適用対象: SQL Server 2019 (15.x) 以降のバージョンと Azure SQL Database
offrow_regular_version_record_count bigint 元のデータ行の外側に保持されているバージョン レコードの数。

適用対象: SQL Server 2019 (15.x) 以降のバージョンと Azure SQL Database
offrow_long_term_version_record_count bigint 長期的と見なされるバージョン レコードの数。

適用対象: SQL Server 2019 (15.x) 以降のバージョンと Azure SQL Database

Note

SQL Server のドキュメントでは、一般にインデックスに関して B ツリーという用語が使用されます。 行ストア インデックスで、SQL Server によって B+ ツリーが実装されます。 これは、列ストア インデックスやメモリ内データ ストアには適用されません。 詳細については、「SQL Server と Azure SQL のインデックスのアーキテクチャとデザイン ガイド」を参照してください。

解説

sys.dm_db_index_physical_stats 動的管理機能は DBCC SHOWCONTIG ステートメントを置き換えます。

スキャン モード

関数が実行されるモードによって、関数で使用する統計データを取得するためのスキャンのレベルが決まります。 モード は、LIMITED、SAMPLED、または DETAILED として指定されます。 この関数は、テーブルまたはインデックスの指定されたパーティションを構成する割り当て単位のページ チェーンを走査します。 sys.dm_db_index_physical_stats では、実行モードに関係なく、Intent-Shared (IS) テーブル ロックのみが必要です。

LIMITED モードは最も高速なモードで、スキャンするページ数は最小です。 インデックスでは、B ツリーの親レベルのページ (リーフ レベルより上のページ) だけがスキャンされます。 ヒープでは、関連付けられた PFS ページおよび IAM ページが調べられます。LIMITED モードではヒープのデータ ページがスキャンされます。

制限モードでは、compressed_page_countデータベース エンジンは B ツリーの非リーフ ページとヒープの IAM ページと PFS ページのみをスキャンするため、NULL です。 SAMPLED モードを使用して推定値 compressed_page_countを取得し、DETAILED モードを使用して実際の値を取得します compressed_page_count。 SAMPLED モードは、インデックスまたはヒープ内のすべてのページの 1% のサンプルに基づいて統計を返します。 SAMPLED モードの結果は近似と見なす必要があります。 インデックスまたはヒープのページ数が 10,000 ページ未満の場合は、SAMPLED ではなく DETAILED モードが使用されます。

DETAILED モードではすべてのページがスキャンされ、すべての統計が返されます。

各モードでより多くの作業が実行されるため、モードは LIMITED から DETAILED に徐々に遅くなります。 テーブルまたはインデックスの断片化レベルのサイズをすばやく計測するには、LIMITED モードを使用します。 これは最も高速であり、インデックスの割り当て単位内の IN_ROW_DATA 非リーフ レベルごとに行を返しません。

システム関数を使用してパラメーター値を指定する

Transact-SQL 関数のDB_IDOBJECT_IDを使用して、database_idパラメーターとobject_id パラメーターの値を指定できます。 ただし、これらの関数に無効な値を渡すと、意図しない結果が発生する可能性があります。 たとえば、データベース名またはオブジェクト名が存在しない、またはスペルが正しくないために見つからない場合、両方の関数は NULL を返します。 この関数は sys.dm_db_index_physical_stats 、NULL を、すべてのデータベースまたはすべてのオブジェクトを指定するワイルドカード値として解釈します。

さらに、OBJECT_ID関数は関数が呼び出される前にsys.dm_db_index_physical_stats処理されるため、database_idで指定されたデータベースではなく、現在のデータベースのコンテキストで評価されます。 この動作により、関数は OBJECT_ID NULL 値を返す可能性があります。または、オブジェクト名が現在のデータベース コンテキストと指定されたデータベースの両方に存在する場合は、エラー メッセージが返される可能性があります。 次の例は、こうした意図しない結果を示すものです。

USE master;
GO
-- In this example, OBJECT_ID is evaluated in the context of the master database.
-- Because Person.Address does not exist in master, the function returns NULL.
-- When NULL is specified as an object_id, all objects in the database are returned.
-- The same results are returned when an object that is not valid is specified.
SELECT * FROM sys.dm_db_index_physical_stats
    (DB_ID(N'AdventureWorks2022'), OBJECT_ID(N'Person.Address'), NULL, NULL , 'DETAILED');
GO
-- This example demonstrates the results of specifying a valid object name
-- that exists in both the current database context and
-- in the database specified in the database_id parameter of the
-- sys.dm_db_index_physical_stats function.
-- An error is returned because the ID value returned by OBJECT_ID does not
-- match the ID value of the object in the specified database.
CREATE DATABASE Test;
GO
USE Test;
GO
CREATE SCHEMA Person;
GO
CREATE Table Person.Address(c1 int);
GO
USE AdventureWorks2022;
GO
SELECT * FROM sys.dm_db_index_physical_stats
    (DB_ID(N'Test'), OBJECT_ID(N'Person.Address'), NULL, NULL , 'DETAILED');
GO
-- Clean up temporary database.
DROP DATABASE Test;
GO

ベスト プラクティス

使用する場合 DB_ID は、必ず有効な ID が返されることを確認してください OBJECT_ID。 たとえば、使用 OBJECT_IDする場合は、次のような OBJECT_ID(N'AdventureWorks2022.Person.Address')3 部構成の名前を指定するか、関数で使用する前に関数によって返される値を sys.dm_db_index_physical_stats テストします。 次の例 A と B は、データベース ID とオブジェクト ID を指定する安全な方法を示しています。

断片化の検出

断片化は、テーブルとテーブルに定義されたインデックスに対して、INSERT、UPDATE、DELETE ステートメントによるデータ変更が行われる過程で発生します。 これらの変更は通常、テーブルとインデックスの行間で均等に分散されないため、各ページのフルネスは時間の経過と同時に変化する可能性があります。 このような断片化があるときに、クエリでテーブルのインデックスの一部または全部をスキャンしようとすると、余分なページの読み取りが必要になり、 データの並列スキャンの妨げになります。

インデックスまたはヒープの断片化レベルが列に avg_fragmentation_in_percent 表示されます。 ヒープの場合、値はヒープのエクステントの断片化を表します。 インデックスの場合、この値はインデックスの論理的な断片化を表します。 異なり DBCC SHOWCONTIG、どちらの場合も断片化計算アルゴリズムでは、複数のファイルにまたがるストレージが考慮されるため、正確です。

論理的な断片化

これは、インデックスのリーフ ページ内の順序の切れたページの割合です。 順序が不正なページとは、インデックスに割り当てられている次の物理的なページと、現在のリーフ ページの next-page ポインターが示すページが異なるページのことです。

エクステントの断片化

これは、ヒープのリーフ ページ内の順序が正しくありません。 順序が正しくないエクステントとは、ヒープの現在のページを含むエクステントが、前のページを含むエクステントの後に物理的に次のエクステントではないエクステントです。

最大のパフォーマンスを得るために、値 avg_fragmentation_in_percent は可能な限りゼロに近い値にする必要があります。 ただし、0% から 10% までの値を許容できる場合があります。 再構築、再構成、再作成など、断片化を解消するためのさまざまな手段を使用することによって、この値を下げることができます。 インデックスの断片化の程度を分析する方法の詳細については、「インデックスの再構成と再構築」を参照してください

インデックスの断片化を減らす

断片化がクエリのパフォーマンスに影響を与える方法でインデックスが断片化されている場合は、断片化を減らすための 3 つの選択肢があります。

  • クラスター化インデックスを削除し、再作成する。

    クラスター化インデックスを再作成すると、データが再分配され、データ ページにデータが満たされます。 ページのゆとりのレベルは、CREATE INDEX の FILLFACTOR オプションを使用して構成できます。 この方法の欠点は、削除および再作成サイクル中にインデックスがオフラインであり、操作がアトミックであるという点です。 インデックスの作成が中断されると、インデックスは再作成されません。 詳細については、「 CREATE INDEX (Transact-SQL)」を参照してください。

  • ALTER INDEX REORGANIZE を使用して、インデックスのリーフ レベル ページを論理的な順序で並べ替えます DBCC INDEXDEFRAG。 これはオンライン操作であるため、ステートメントの実行中にインデックスを使用できます。 操作を中断しても、それまでに完了した作業は失われません。 この方法の欠点は、インデックス再構築操作としてデータを再構成するジョブが適切に行われず、統計が更新されないという点です。

  • 代替の ALTER INDEX REBUILD を DBCC DBREINDEX使用して、インデックスをオンラインまたはオフラインで再構築します。 詳細については、「ALTER INDEX (Transact-SQL)」を参照してください。

断片化だけでは、インデックスを再構成または再構築するための十分な理由ではありません。 断片化の主な効果は、インデックス スキャン中にページ先行読み取りスループットが低下することです。 これは、応答時間の遅れの原因となります。 フラグメント化されたテーブルまたはインデックスに対するクエリ ワークロードにスキャンが含まれていない場合、ワークロードは主にシングルトン参照であるため、断片化を削除しても効果がない可能性があります。

Note

DBCC SHRINKDATABASE圧縮操作中にインデックスが部分的または完全に移動された場合に、実行中DBCC SHRINKFILEまたは断片化が発生する可能性があります。 そのため、圧縮操作を実行する必要がある場合は、断片化が削除される前に行う必要があります。

ヒープ内の断片化を減らす

ヒープのエクステントの断片化を減らすには、テーブルにクラスター化インデックスを作成し、インデックスを削除します。 これによって、クラスター化インデックスの作成中にデータが再分配されます。 この操作では、データベースの空き領域の分布を考慮に入れて、可能な限り最適化も行われます。 クラスター化インデックスを削除してヒープを再作成すると、データは移動されず、最適な位置に残ります。 これらの操作を実行する方法については、CREATE INDEX と DROP INDEX を参照してください

注意事項

テーブルでクラスター化インデックスを作成および削除すると、そのテーブル上のすべての非クラスター化インデックスが 2 回再構築されます。

ラージ オブジェクト データの圧縮

既定では、ALTER INDEX REORGANIZE ステートメントは、ラージ オブジェクト (LOB) データを含むページを圧縮します。 LOB ページは空のときは割り当てが解除されないため、このデータを圧縮すると、多数の LOB データが削除された場合や LOB 列が削除された場合に、ディスク領域の使用が向上する可能性があります。

指定されたクラスター化インデックスを再構成すると、クラスター化インデックスに含まれるすべての LOB 列が圧縮されます。 非クラスター化インデックスを再構成すると、そのインデックス内で非キー列 (付加列) となっているすべての LOB 列が圧縮されます。 ステートメントで ALL を指定した場合は、指定したテーブルまたはビューに関連付けられているすべてのインデックスが再構成されます。 さらに、クラスター化インデックス、基になるテーブル、または含まれる列を含む非クラスター化インデックスに関連付けられているすべての LOB 列が圧縮されます。

ディスク領域の使用を評価する

この列は avg_page_space_used_in_percent 、ページの満杯を示します。 最適なディスク領域の使用を実現するには、ランダムな挿入が多くないインデックスの場合、この値は 100% に近い必要があります。 ただし、多数のランダム挿入があり、ページ全体が非常に多いインデックスでは、ページ分割の数が増えます。 断片化が大きくなります。 したがって、ページ分割を減らすには、値を 100% 未満にする必要があります。 FILLFACTOR オプションを指定してインデックスを再構築すると、ページのゆとりをインデックスのクエリ パターンに合わせて変更できます。 塗りつぶし係数の詳細については、「インデックスの塗りつぶし係数を指定する」を参照してください。 また、ALTER INDEX REORGANIZE は最後に指定された FILLFACTOR までページが埋まるよう、インデックスを圧縮します。 これにより、avg_space_used_in_percent の値は増加します。 ALTER INDEX REORGANIZE では、ページのフルネスを減らすことはできません。 代わりに、インデックスの再構築を実行する必要があります。

インデックス フラグメントを評価する

フラグメントは、アロケーション ユニットの同じファイル内の物理的に連続するリーフ ページで構成されます。 1 つのインデックスには少なくとも 1 つのフラグメントが含まれます。 インデックスが持つことのできるフラグメントの最大数は、インデックスのリーフ レベルのページ数と同じです。 フラグメントが大きいほど、同じ数のページの読み取りに必要なディスクの I/O が少なくなります。 したがって、値が avg_fragment_size_in_pages 大きいほど、範囲スキャンのパフォーマンスが向上します。 値 avg_fragment_size_in_pagesavg_fragmentation_in_percent 値は相互に反比例します。 そのため、インデックスを再構築または再構成すると、断片化の量を減らし、フラグメント サイズを増やす必要があります。

制限事項と制約事項

クラスター化列ストア インデックスのデータは返されません。

アクセス許可

次のアクセス許可が必要です。

  • データベース内の指定したオブジェクトに対する CONTROL 権限。

  • オブジェクト ワイルドカード @object_id=NULL を使用して、指定したデータベース内のすべてのオブジェクトに関する情報を返す VIEW DATABASE STATE または VIEW DATABASE PERFORMANCE STATE (SQL Server 2022) 権限。

  • データベース ワイルドカード @database_id = NULL を使用して、すべてのデータベースに関する情報を返す VIEW SERVER STATE または VIEW SERVER PERFORMANCE STATE (SQL Server 2022) 権限。

VIEW DATABASE STATE を付与すると、特定のオブジェクトに対して拒否された CONTROL 権限に関係なく、データベース内のすべてのオブジェクトを返すことができます。

VIEW DATABASE STATE 権限を拒否すると、特定のオブジェクトに対する CONTROL 権限が許可されていたとしても、そのデータベース内のどのオブジェクトも取得できません。 また、データベース ワイルドカード @database_id=NULL を指定した場合、データベースは省略されます。

詳細については、「動的管理ビューと動的管理関数 (Transact-SQL)」を参照してください

A. 指定したテーブルに関する情報を返します

次の例では、Person.Address テーブルのすべてのインデックスとパーティションについて、サイズと断片化の統計を返します。 スキャン モードは、最適なパフォーマンスを得るために設定 'LIMITED' され、返される統計情報を制限します。 このクエリを実行するには、少なくとも Person.Address テーブルに対する CONTROL 権限が必要です。

DECLARE @db_id SMALLINT;
DECLARE @object_id INT;
  
SET @db_id = DB_ID(N'AdventureWorks2022');
SET @object_id = OBJECT_ID(N'AdventureWorks2022.Person.Address');
  
IF @db_id IS NULL
BEGIN;
    PRINT N'Invalid database';
END;
ELSE IF @object_id IS NULL
BEGIN;
    PRINT N'Invalid object';
END;
ELSE
BEGIN;
    SELECT * FROM sys.dm_db_index_physical_stats(@db_id, @object_id, NULL, NULL , 'LIMITED');
END;
GO

B. ヒープに関する情報を返す

次の例では、AdventureWorks2022 データベース内のヒープ dbo.DatabaseLog のすべての統計情報を返します。 テーブルには LOB データが含まれているため、ヒープのデータ ページを LOB_DATA 格納している行に対して返される IN_ROW_ALLOCATION_UNIT 行に加えて、割り当て単位の行が返されます。 このクエリを実行するには、少なくとも dbo.DatabaseLog テーブルに対する CONTROL 権限が必要です。

DECLARE @db_id SMALLINT;
DECLARE @object_id INT;
SET @db_id = DB_ID(N'AdventureWorks2022');
SET @object_id = OBJECT_ID(N'AdventureWorks2022.dbo.DatabaseLog');
IF @object_id IS NULL
BEGIN;
    PRINT N'Invalid object';
END;
ELSE
BEGIN;
    SELECT * FROM sys.dm_db_index_physical_stats(@db_id, @object_id, 0, NULL , 'DETAILED');
END;
GO

C: すべてのデータベースの情報を返す

次の例では、すべてのパラメーターにワイルドカード NULL を指定して、SQL Server のインスタンス内のすべてのテーブルとインデックスのすべての統計情報を返します。 このクエリを実行するには、VIEW SERVER STATE 権限が必要です。

SELECT * FROM sys.dm_db_index_physical_stats (NULL, NULL, NULL, NULL, NULL);
GO

D. スクリプトでインデックスを再構築または再構成する場合に使用 sys.dm_db_index_physical_stats する

次の例では、平均断片化が 10% を超えるデータベース内のすべてのパーティションを自動的に再構成または再構築します。 このクエリを実行するには、VIEW DATABASE STATE 権限が必要です。 この例では、データベース名を指定せずに DB_ID を 1 番目のパラメーターとして指定しています。 現在のデータベースの互換性レベルが 80 以下の場合、エラーが生成されます。 このエラーを解決するには、DB_ID() を有効なデータベース名で置き換えます。 データベース互換性レベルの詳細については、「ALTER DATABASE 互換性レベル (Transact-SQL)」を参照してください

-- Ensure a USE <databasename> statement has been executed first.
SET NOCOUNT ON;

DECLARE @objectid INT;
DECLARE @indexid INT;
DECLARE @partitioncount BIGINT;
DECLARE @schemaname NVARCHAR(130);
DECLARE @objectname NVARCHAR(130);
DECLARE @indexname NVARCHAR(130);
DECLARE @partitionnum BIGINT;
DECLARE @partitions BIGINT;
DECLARE @frag FLOAT;
DECLARE @command NVARCHAR(4000);

-- Conditionally select tables and indexes from the sys.dm_db_index_physical_stats function
-- and convert object and index IDs to names.
SELECT object_id AS objectid,
    index_id AS indexid,
    partition_number AS partitionnum,
    avg_fragmentation_in_percent AS frag
INTO #work_to_do
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED')
WHERE avg_fragmentation_in_percent > 10.0
    AND index_id > 0;

-- Declare the cursor for the list of partitions to be processed.
DECLARE partitions CURSOR
FOR
SELECT *
FROM #work_to_do;

-- Open the cursor.
OPEN partitions;

-- Loop through the partitions.
WHILE (1 = 1)
BEGIN;

    FETCH NEXT
    FROM partitions
    INTO @objectid,
        @indexid,
        @partitionnum,
        @frag;

    IF @@FETCH_STATUS < 0
        BREAK;

    SELECT @objectname = QUOTENAME(o.name),
        @schemaname = QUOTENAME(s.name)
    FROM sys.objects AS o
    INNER JOIN sys.schemas AS s
        ON s.schema_id = o.schema_id
    WHERE o.object_id = @objectid;

    SELECT @indexname = QUOTENAME(name)
    FROM sys.indexes
    WHERE object_id = @objectid
        AND index_id = @indexid;

    SELECT @partitioncount = count(*)
    FROM sys.partitions
    WHERE object_id = @objectid
        AND index_id = @indexid;

    -- 30 is an arbitrary decision point at which to switch between reorganizing and rebuilding.
    IF @frag < 30.0
        SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REORGANIZE';

    IF @frag >= 30.0
        SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD';

    IF @partitioncount > 1
        SET @command = @command + N' PARTITION=' + CAST(@partitionnum AS NVARCHAR(10));

    EXEC (@command);

    PRINT N'Executed: ' + @command;
END;

-- Close and deallocate the cursor.
CLOSE partitions;

DEALLOCATE partitions;

-- Drop the temporary table.
DROP TABLE #work_to_do;
GO

E. ページ圧縮ページの数を表示するために使用 sys.dm_db_index_physical_stats します

次の例は、ページの合計数を、行とページが圧縮されたページと比較する方法を示しています。 この情報は、圧縮によってインデックスまたはテーブルに対して提供される利点を判断するために使用できます。

SELECT o.name,
    ips.partition_number,
    ips.index_type_desc,
    ips.record_count,
    ips.avg_record_size_in_bytes,
    ips.min_record_size_in_bytes,
    ips.max_record_size_in_bytes,
    ips.page_count,
    ips.compressed_page_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') ips
INNER JOIN sys.objects o
    ON o.object_id = ips.object_id
ORDER BY record_count DESC;

F. SAMPLED モードで使用 sys.dm_db_index_physical_stats する

次の例は、SAMPLED モードが DETAILED モードの結果とは異なる近似値を返す方法を示しています。

CREATE TABLE t3 (
    col1 INT PRIMARY KEY,
    col2 VARCHAR(500)
    )
    WITH (DATA_COMPRESSION = PAGE);
GO

BEGIN TRANSACTION

DECLARE @idx INT = 0;

WHILE @idx < 1000000
BEGIN
    INSERT INTO t3 (col1, col2)
    VALUES (
        @idx,
        REPLICATE('a', 100) + CAST(@idx AS VARCHAR(10)) + REPLICATE('a', 380)
        )

    SET @idx = @idx + 1
END

COMMIT;
GO

SELECT page_count,
    compressed_page_count,
    forwarded_record_count,
    *
FROM sys.dm_db_index_physical_stats(db_id(), object_id('t3'), NULL, NULL, 'SAMPLED');

SELECT page_count,
    compressed_page_count,
    forwarded_record_count,
    *
FROM sys.dm_db_index_physical_stats(db_id(), object_id('t3'), NULL, NULL, 'DETAILED');

G. インデックスの断片化のために Service Broker キューにクエリを実行する

適用対象: SQL Server 2016 (13.x) 以降のバージョン。

次の例は、サーバー ブローカー キューに対して断片化のクエリを実行する方法を示しています。

--Using queue internal table name
SELECT *
FROM sys.dm_db_index_physical_stats(db_id(), object_id('sys.queue_messages_549576996'), DEFAULT, DEFAULT, DEFAULT);

--Using queue name directly
SELECT *
FROM sys.dm_db_index_physical_stats(db_id(), object_id('ExpenseQueue'), DEFAULT, DEFAULT, DEFAULT);

関連項目