非常に重要な概念であるため、非常に良い質問です。ただし、これは大きなトピックであり、基本概念を理解できるように簡略化して説明します。
まず、クラスター化インデックス思考テーブルが表示されます。SQLサーバーでは、テーブルにクラスター化インデックスが含まれていない場合、それはヒープです。テーブルにクラスター化インデックスを作成すると、実際にはテーブルがBツリータイプの構造に変換されます。クラスター化インデックスはテーブルであり、テーブルとは別ではありません
クラスタ化インデックスを1つしか持てないのはなぜだろうと思ったことはありませんか?クラスター化インデックスが2つある場合、テーブルのコピーが2つ必要になります。結局データが含まれています。
これを簡単な例を使用して説明します。
注:この例でテーブルを作成し、300万件以上のランダムエントリを入力しました。次に、実際のクエリを実行し、実行計画をここに貼り付けました。
本当に把握する必要があるのは、表記法または運用効率です。次の表があると仮定しましょう。
CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
したがって、ここには、CustomerIDにクラスター化されたキーを持つ基本テーブルがあります(プライマリーキーはデフォルトでクラスター化されています)。したがって、テーブルは主キーCustomerIDに基づいて配置/順序付けされます。中間レベルには、CustomerID値が含まれます。データページには行全体が含まれるため、テーブル行になります。
また、CustomerNameフィールドに非クラスター化インデックスを作成します。次のコードで実行します。
CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer]
(
[CustomerName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
, DROP_EXISTING = OFF, ONLINE = OFF
, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
そのため、このインデックスでは、データページ/リーフレベルのノードに、クラスター化インデックスの中間レベルへのポインターがあります。インデックスは、CustomerNameフィールドの周りに配置/順序付けされます。したがって、中間レベルにはCustomerName値が含まれ、リーフレベルにはポインターが含まれます(これらのポインター値は、実際には主キー値またはCustomerID列です)。
そうです、次のクエリを実行すると:
SELECT * FROM Customer WHERE CustomerID = 1
SQLはおそらく、シーク操作を介してクラスター化インデックスを読み取ります。シーク操作は、順次検索であるスキャンよりもはるかに効率的なバイナリ検索です。したがって、上記の例では、インデックスが読み取られ、バイナリ検索を使用することで、SQLは探している基準に一致しないデータを削除できます。クエリプランについては、添付のスクリーンショットを参照してください。
したがって、シーク操作の操作数またはO表記は次のとおりです。
- 検索対象の値を中間レベルの値と比較して、クラスター化インデックスでバイナリ検索を実行します。
- 一致する値を返す
したがって、2つの操作です。ただし、次のクエリを実行した場合:
SELECT * FROM Customer WHERE CustomerName ='John'
SQLは、CustomerNameの非クラスター化インデックスを使用して検索を実行します。ただし、これは非クラスター化インデックスであるため、行のすべてのデータが含まれているわけではありません。
そのため、SQLは中間レベルで検索を行って一致するレコードを見つけ、返された値を使用して検索を実行し、クラスター化インデックス(別名テーブル)で別の検索を行って実際のデータを取得します。これはわかりにくいですが、読み進めていくと明らかになります。
非クラスター化インデックスには、CustomerNameフィールド(中間ノードに格納されているインデックスフィールド値)とCustomerIDであるデータへのポインターのみが含まれているため、インデックスにはCustomerSurnameのレコードがありません。CustomerSurnameは、クラスター化インデックスまたはテーブルから取得する必要があります。
このクエリを実行すると、次の実行計画が得られます。
上記のスクリーンショットで気付くべき2つの重要なことがあります
- SQLには、インデックスがありません(緑色のテキスト)と言われています。SQLは、CustomerIDとCustomerSurnameを含むCustomerNameにインデックスを作成することを提案しています。
- また、クエリの時間の99%が主キーインデックス/クラスター化インデックスのキールックアップに費やされていることがわかります。
SQLがCustomerNameのインデックスを再度提案するのはなぜですか?インデックスにはCustomerIDのみが含まれており、CustomerName SQLはテーブル/クラスター化インデックスからCustomerSurnameを見つける必要があるためです。
インデックスを作成し、CustomerSurname列をインデックスに含めた場合、SQLは非クラスター化インデックスを読み取るだけでクエリ全体を満たすことができます。これが、SQLが非クラスター化インデックスの変更を提案している理由です。
ここでは、クラスター化されたキーからCustomerSurname列を取得するためにSQLが必要とする追加の操作を確認できます。
したがって、操作の数は次のとおりです。
- 検索対象の値を中間レベルの値と比較して、非クラスター化インデックスでバイナリ検索を実行します
- 一致するノードの場合、クラスター化インデックス内のデータへのポインターを含むリーフレベルノードを読み取ります(リーフレベルノードには、主キー値が含まれます)。
- 返される値ごとに、クラスター化インデックス(テーブル)で読み取りを実行し、ここで行の値を取得します。CustomerSurnameを読み取ります。
- 一致する行を返す
値を取得するための4つの操作です。クラスター化インデックスの読み取りと比較して、必要な操作の2倍。すべてのデータが含まれているため、クラスター化インデックスが最も強力なインデックスであることを示しています。
最後のポイントを明確にするために。非クラスター化インデックスのポインターが主キー値であると言うのはなぜですか?非クラスター化インデックスのリーフレベルノードにプライマリキー値が含まれていることを示すために、クエリを次のように変更します。
SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'
このクエリでは、SQLは非クラスター化インデックスからCustomerIDを読み取ることができます。クラスタ化インデックスを検索する必要はありません。これは、次のような実行計画で確認できます。
このクエリと前のクエリの違いに注意してください。ルックアップはありません。SQLは、非クラスター化インデックス内のすべてのデータを検索できます。
クラスター化インデックスはテーブルであり、非クラスター化インデックスにはすべてのデータが含まれていないことを理解できることを願っています。バイナリ検索は実行できますが、クラスター化インデックスのみにすべてのデータが含まれているため、インデックス作成は選択を高速化します。そのため、非クラスター化インデックスを検索すると、ほとんどの場合、クラスター化インデックスから値が読み込まれます。これらの追加の操作により、非クラスター化インデックスはクラスター化インデックスよりも効率が低下します。
これで問題が解決することを願っています。何か意味がわからない場合は、コメントを投稿してください。明確にしようとします。ここではかなり遅れており、私の脳は少し平らに感じています。レッドブルの時間。