SQL Server如何确定查询计划缺少索引建议中的键列顺序?
解决方法
当SQL Server为特定查询计划创建缺少索引建议时,它会将可能的键列分为2组.第一组包含属于EQUALITY谓词的所有推荐列.第二组包含属于INEQUALITY谓词的所有推荐列.
在每个集合中,列根据表定义按列的序号位置排序.
(非常感谢Brent Ozar构建针对Stack Overflow数据库的repro脚本来证明这一点!)
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,AboutMe NVARCHAR(MAX)); GO CREATE TABLE dbo.DateNumberLetter (ID INT IDENTITY(1,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,AboutMe FROM dbo.Users WITH (NOLOCK) ORDER BY Id; GO INSERT INTO dbo.DateNumberLetter(fINT,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
所有三个表都具有完全相同的数据,并且查询完全相同.唯一的区别是字段顺序 – 这也是我们缺少索引请求的差异:
在执行计划中,缺失索引请求中的列顺序与表中的列顺序完全匹配.例如,在dbo.NumberLetterDate中,数字列是第一个,因此它也是缺少索引请求中的第一个:
>在dbo.NumberLetterDate上,缺少的索引在fINT(数字),fLetter(nvarchar),fDate上,与表中字段的顺序相同
>在dbo.LetterDateNumber上,索引顺序切换到fNVARCHAR,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个缺失索引请求中显示最后,因为它是不等式搜索:
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个字段在缺失索引建议中具有相同的优先级顺序,现在我们回到纯粹按字段顺序排序: