私は最近、DBのさまざまなビューのインデックス作成に取り組み始めました。多くの場合、これらのテーブルの一部は結合にのみ使用され、ステートメントのwhereまたはorderで使用されることはまれです。つまり、すべて主キーをキーとする複数のインデックス(または比較的効率の低い1つの大きなインデックス)を作成しています。このテーブルから何かを含めることによる場所や順序がなく、問題が発生しているビューにキールックアップがないことを明確にする必要があります。
SQLにすべてtbl.IDをキーとする複数のインデックスがある場合、1つのインデックスを他のインデックスよりも使用することをどのように選択しますか?インデックスから引き出されたデータが同じであっても、ビュー間で異なる可能性があることがわかりました。一般に、あるインデックスを他のインデックスよりも選択しても、効率に大きな利益/損失はありません。 「十分に良い」インデックスを選択して次に進むと私に信じさせます(同じ列から多くのキーオフがあるため)。
さらに、この理由により、同じキーで複数のインデックスを作成することは避けるべきですか?
私が現在扱っている特定の問題には、次のようなインデックスを選択するという見方があります。
CREATE NONCLUSTERED INDEX [index1]
ON a.tbl ([ID])
INCLUDE (number,name,year, ...)
インクルードに14列がある場合(残念ですが、現在のクエリ構造に基づいて必要です)
より効率的なインデックス:
CREATE NONCLUSTERED INDEX [index2]
ON a.tbl ([ID])
INCLUDE (number,name,year, ...)
インクルードに5列しかない場合。
どちらのインデックスにもビューに必要な情報がありますが、明らかに1つははるかに小さく、より効率的に使用できます。その中のすべての列(表の約半分)を呼び出す大規模なビューをカバーするには、より大きな索引が存在する必要があります。
上記のように1つの大規模なインデックスを作成することを選択できることはおそらく言及する価値があります。私のテストによると、これらの追加のインデックスを追加しても、テーブルの更新に大きなコストはかからないようです。パフォーマンスを少し向上させるために作成することにしました。一般に、これらは必要に応じて使用されますが、特定の場合、オプティマイザは明確な理由なしに効率の低いオプションを選択します。
私の限られた実験では:
これは理にかなっています。これらのいずれかの場合に完全なインデックスを検索するルールを追加する場合は、最も節約できるときに実行する必要があります。節約の観点からそれほど問題にならない場合に、より良いインデックスを見つけるために費やされる労力を求めてすべての行に対してとは対照的に行ごとにははるかに可能性が低くなります価値がある。 「最後に」はおそらく意図的なものであり、最後に作成したカバリングインデックスがおそらくあなたが作成した最高のものであるか、または任意で偶然である(欠落しているインデックス推奨の列の順序など)と想定しています。
私のテストのセットアップは非常に簡単でした:
CREATE TABLE dbo.t1
(
id int IDENTITY(1,1) PRIMARY KEY,
sn1 sysname, tn1 sysname, cn1 sysname, typ1 sysname,
sn2 sysname, tn2 sysname, cn2 sysname, typ2 sysname,
sn3 sysname, tn3 sysname, cn3 sysname, typ3 sysname,
sn4 sysname, tn4 sysname, cn4 sysname, typ4 sysname
);
GO
SET NOCOUNT ON;
GO
INSERT dbo.t1
(
sn1,tn1,cn1,typ1,sn2,tn2,cn2,typ2,
sn3,tn3,cn3,typ3,sn4,tn4,cn4,typ4
)
SELECT s.name, t.name, c.name, typ.name,
s.name, t.name, c.name, typ.name,
s.name, t.name, c.name, typ.name,
s.name, t.name, c.name, typ.name
FROM sys.schemas AS s
CROSS JOIN sys.objects AS t
INNER JOIN sys.all_columns AS c
ON t.[object_id] = c.[object_id]
INNER JOIN sys.types AS typ
ON c.user_type_id = typ.user_type_id;
GO
CREATE VIEW dbo.v1
AS
SELECT id,sn1,tn1
FROM dbo.t1;
GO
これにより、13,260行がテーブルに挿入されます(結果は異なります)。次に、同じ3つのインデックスを異なる順序で繰り返し作成しました。
-- widest first
CREATE INDEX ix_wide ON dbo.t1(sn1)
INCLUDE(tn1,cn1,typ1,sn2,tn2,cn2,typ2,sn3,tn3,cn3,typ3,sn4,tn4,cn4,typ4);
GO
CREATE INDEX ix_mid ON dbo.t1(sn1) INCLUDE(tn1,cn1,sn2,tn2,cn2,typ2,sn3,tn3);
GO
CREATE INDEX ix_small ON dbo.t1(sn1) INCLUDE(tn1,cn1);
GO
-- widest last = ix_small then ix_mid then ix_wide
-- middle1 = ix_mid then ix_wide then ix_small
-- middle2 = ix_small then ix_wide then ix_mid
次に、これらの4つのケースのそれぞれで、これら2つのクエリを実行して計画を調査しました。
DBCC FREEPROCCACHE;
GO
SELECT id,sn1,tn1 FROM dbo.v1; -- scan
GO
SELECT id,sn1,tn1 FROM dbo.v1 WHERE sn1 LIKE N'q%'; -- seek
結果:
widest first widest last middle1 middle2
------- ------------ ----------- -------- --------
scan ix_small ix_small ix_small ix_small
seek ix_small ix_wide ix_small ix_mid
スキャンは常に最も狭いインデックスを選択しました。シークは常に、最後に作成されたインデックスを選択しました(すべてのカバー以降)。この結果が変わるとは思わないので、テストに拡張して非カバーインデックスをミックスに含めることはしませんでした。
ここには2つの道徳があります:
おそらく、Paulが明確にするためにここで見逃しているいくつかの内部とヒューリスティック。また、トレースフラグ302を有効にしてインデックス選択情報を出力しようとしましたが、これはSQL Serverの最新バージョンでは機能しないようです。