SQL Serverは、クエリプランの推奨されないインデックスのキー列の順序をどのように決定しますか?
SQL Serverは、クエリプランの推奨されないインデックスのキー列の順序をどのように決定しますか?
回答:
SQL Serverは、特定のクエリプランに対して不足しているインデックスの推奨事項を作成するときに、可能なキー列を2つのグループに分けます。最初のセットには、EQUALITY述部の一部である推奨列がすべて含まれています。2番目のセットには、INEQUALITY述部の一部である推奨列がすべて含まれています。
各セット内で、列は、テーブル定義に基づいて、列の順序位置によって順序付けられます。
(これを証明するために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つのテーブルはすべてまったく同じデータを持ち、クエリは同一です。唯一の違いはフィールドの順序です。これは、欠落しているインデックスリクエストの違いでもあります。
実行プランでは、欠落しているインデックスリクエストの列の順序はテーブルの列の順序と正確に一致します。たとえば、dbo.NumberLetterDateでは、数値列が最初であるため、欠落したインデックスリクエストでも最初にあります。
このような単一テーブル操作の場合、インデックスフィールドの順序は、選択性、データ型、またはクエリ内の位置に依存するようには見えません。(より複雑なクエリと結合でこれを証明するために、他の人に任せます。)
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つの欠落したインデックスリクエストすべてで最後に表示されます。
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つのフィールドすべてが、欠落しているインデックスの推奨で同じ優先順位を持ち、フィールド順による純粋なソートに戻ります。