web-dev-qa-db-ja.com

SQL Serverがクラスター化インデックスよりも非クラスター化インデックスを優先するのはなぜですか?

私はテーブルを高速化しようとしています、そして、実験していると、私はこれ(私が思うこと)の奇妙な発生に遭遇しました。同じものである必要があるクラスタ化インデックスと非クラスタ化インデックスを作成しました。ただし、テーブルに対してクエリを実行したところ、SQL Serverは常に、一致するクラスター化インデックスの代わりに非クラスター化インデックスを使用することを望んでいることがわかりました。さらに、必要な場合、SQL Serverは非クラスター化インデックスに対して適切にインデックスシークを実行しますが、常にクラスター化インデックスに対してスキャンを実行します。

SQL Serverが非クラスター化インデックスを好む理由

そして、これをどのように書き換えれば、パフォーマンスは向上しますが、クラスター化インデックスのみが得られますか?

次のテーブル構造があります。

_CREATE TABLE [dbo].[Variables](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [Header] [varchar](255) NULL,
    [FullVariables] [varchar](max) NULL
)
_

クラスター化インデックス:

_ALTER TABLE [dbo].[Variables] ADD  CONSTRAINT [PK_Variables] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)
_

非クラスター化インデックス:

_CREATE UNIQUE NONCLUSTERED INDEX [NonClusteredIndex-20190307-091011] ON [dbo].[Variables]
(
    [ID] ASC
)
INCLUDE (   [Header],
    [FullVariables]) 
_

私の現在の知識では、この場合、これらのインデックスの両方に同じ方法でレイアウトされたデータが含まれている必要があると考えています。[ID]がキー列で、代わりに[Header]と[FullVariables]がインデックスに含まれる追加データとしてポインタであることの。リンクできる知識のソースがある場合、私は詳細を読みたいと思っています。

私は常にシークを望んでいるわけではなく、スキャンの方が良い場合があることを理解する必要があります(そうでない場合、なぜスキャンが必要になるのか)。行サイズ(数百万)にvarchar(MAX)(16000文字以上の文字列を含む)を掛けたため、テーブルには約60GBのデータが含まれています。テーブルに挿入する前に、重複が挿入されていないことを確認するためにスキャンが行われます(除去のためにHeaderで一致し、FullVariablesで一致します)。次に、シークが必要なIDフィールドのいくつかのビューでテーブルが結合されます。

4
dev

SQL Serverに選択する2つのインデックスがあり、どちらもクエリを満たし(「カバー」)、行を検索またはソートするための最良のパスを提供する場合、検討する必要がありますコイン投げである。それはそうではありませんが...私がここで行ったいくつかの研究があったと思います(たぶん私が herehere )作成した最新のものまたは最初のものを選んだことを示しましたアルファベット順またはそれ以外の場合は任意の何か。

ただし、ここで呼ぶコインフリップが非クラスター化インデックスとクラスター化インデックスのどちらかを選択し、両方のインデックスがクエリを適切に満たしている場合、SQL Serverは常に非クラスター化に傾倒します。どうして?それは、クラスタ化インデックスよりも広くないことが保証されているためです。クラスター化インデックスとまったく同じ幅であるエッジの場合は考慮されません。

各実行プランに関連するコストを確認し、非クラスター化インデックスのSQL Serverの推定コストがクラスター化インデックスのコスト以下であることを確認する必要があります。推定コストがクラスター化よりも高いにもかかわらず、非クラスター化インデックスが選択される反例を示すことができる場合は、実行してください。

4
Aaron Bertrand

SQL Serverに非クラスター化インデックスの使用を断固として停止させる最良の方法は、インデックスを削除することです。非クラスター化インデックスの代わりにクラスター化インデックスを読みたい理由についての質問は不明確です。

SQL Serverは、非クラスター化インデックスを使用する方が高速でありながら、必要な正確な結果を返すと考えています。では、なぜクラスタ化インデックスをスキャンしてSQL Serverが結果をより遅く返すようにするのでしょうか。

質問に正確なクエリを追加し、計画を https://www.brentozar.com/pastetheplan にアップロードします

2
Max Vernon

500,000レコードの同じインデックスをテーブルに入力しました。

次に、このクエリを実行し、

_-- For non clustered index
SELECT index_level
    ,index_type_desc
    ,alloc_unit_type_desc
    ,page_count
    ,record_count
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('Variables'), 2, 1, 'DETAILED')

-- For  clustered index
SELECT index_level
    ,index_type_desc
    ,alloc_unit_type_desc
    ,page_count
    ,record_count
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('Variables'), 1, 1, 'DETAILED')
_

_Page Count_の場合、_Clustered index_が_Non Clustered index_よりわずかに大きいことに気づきました。

したがって、オプティマイザが_Non Clustered index_の場合、_Clustered Index_よりも少ないページを読み取る必要があると計算する可能性があります。

同じ理由で、両方の列がVarchar(100)程度であったとしても、非クラスター化インデックスもクラスター化インデックスよりも優先されます。

_Leaf page_の_Clustered Index_には、他の列を含める必要があります。

_Leaf page_の_Non clustered Index_には、_ID value_と_Clustered Index key_のみが含まれます

したがって、_Clustered Index_ページ数は_Non Clustered index_より大きくなります。

オプティマイザが非クラスタ化インデックスを好む理由は、そのような状況では好まれます。

テーブルに挿入する前に、重複が挿入されていないことを確認するためにスキャンが行われます(削除のためのヘッダーとFullVariablesのマッチング)。

この行は明確ではありません。ヘッダーでのみ重複データをチェックしますか、それとも両方の列(HeaderとFullVariables)でチェックしますか?

ここでクエリの使用を共有できますか?

1
KumarHarsh