web-dev-qa-db-ja.com

SQL Serverは、欠落しているインデックス要求のキー列の順序をどのように決定しますか?

SQL Serverは、クエリプランの欠落しているインデックスの推奨におけるキー列の順序をどのように決定しますか?

32
Bryan Rebok

SQL Serverが特定のクエリプランに対して欠落しているインデックスの推奨を作成するとき、SQL Serverは可能なキー列を2つのグループに分けます。最初のセットには、EQUALITY述語の一部である推奨列がすべて含まれています。 2番目のセットには、不等式述部の一部である推奨される列がすべて含まれています。

各セット内で、列は、テーブル定義に基づいて、列の序数位置によって並べ替えられます。

(これを証明するためにStack Overflowデータベースに対して再現スクリプトを作成してくれたBrent Ozarに感謝します!)

1。 3つの同一のテーブルを作成しますが、列の順序を変えます。 (ここでの理由は、さまざまな列名とデータ型を使用して、欠落しているインデックスの推奨における列の順序に影響しないことを示すためです。)

CREATE TABLE dbo.NumberLetterDate (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, 
fINT INT, fNVARCHAR NVARCHAR(40), fDATE DATETIME, AboutMe NVARCHAR(MAX));
GO
CREATE TABLE dbo.LetterDateNumber (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, 
fNVARCHAR NVARCHAR(40), fDATE DATETIME, fINT INT, AboutMe NVARCHAR(MAX));
GO
CREATE TABLE dbo.DateNumberLetter (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
fDATE DATETIME, fINT INT, fNVARCHAR NVARCHAR(40), AboutMe NVARCHAR(MAX));
GO

2。テーブルに同じデータを入力します。実際のデータ分布を使用して、Usersテーブルから100,000行を取得します。

INSERT INTO dbo.NumberLetterDate(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO
INSERT INTO dbo.LetterDateNumber(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO
INSERT INTO dbo.DateNumberLetter(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
  FROM dbo.Users WITH (NOLOCK)
  ORDER BY Id;
GO

3。インデックスを必要とするクエリを記述します。3つの等式フィルターで開始し、3つのフィールドすべての正確な値をフィルタリングします。 3つのクエリすべてが同じ順序で同じフィールドを持っていることに注意してください。

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT = 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);
GO

3つのテーブルはすべてまったく同じデータを持ち、クエリは同一です。唯一の違いはフィールドの順序です。これが、欠落しているインデックスリクエストの違いでもあります。

Execution plans with 3 equality fields

実行プランでは、欠落したインデックスリクエストの列の順序は、テーブルの列の順序と正確に一致します。たとえば、dbo.NumberLetterDateでは、number列が最初であるため、不足しているインデックスリクエストでも最初になります。

  • Dbo.NumberLetterDateでは、不足しているインデックスはfINT(数値)、fLetter(nvarchar)、fDateにあり、テーブル内のフィールドと同じ順序です
  • Dbo.LetterDateNumberでは、インデックスの順序がfNVARCHAR、fDATE、fINTに切り替わります
  • Dbo.DateNumberLetterでは、インデックスの順序がfDATE、fINT、fNVARCHARに切り替わります

このような単一テーブル操作の場合、インデックスフィールドの順序は、クエリの選択性、データ型、または位置に依存していないようです。 (より複雑なクエリと結合でこれを証明するために、私は他の人に任せます。)

4。不等式フィルターを混合します。たとえば、INTフィールドで、フィルターとして<> 100を入力します。

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT <> 100
  AND fNVARCHAR = 'Brent Ozar'
  AND fDATE = '2018/01/01'
  AND 1 = (SELECT 1);
GO

実行プランでは、等値フィールドが最初に、次に不等式フィールドが表示されます。したがって、ここでは、fINTは不等式検索であるため、不足している3つのインデックス要求すべてで最後に表示されます。

Execution plans with 2 equality, and 1 inequality search

5。 3つの不等式フィルターを使用します。すべてのフィールドに同じ検索を使用します(<>):

SELECT ID
  FROM dbo.NumberLetterDate
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.LetterDateNumber
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);

SELECT ID
  FROM dbo.DateNumberLetter
  WHERE fINT <> 100
  AND fNVARCHAR <> 'Brent Ozar'
  AND fDATE <> '2018/01/01'
  AND 1 = (SELECT 1);
GO

等価検索がないため、欠落しているインデックスの推奨事項では3つのフィールドすべてに同じ優先順位が設定されています。これで、純粋にフィールドの順序で並べ替えることに戻ります。

Execution plans with 3 inequality searches

44
Bryan Rebok