为啥这是索引扫描而不是索引查找

Posted

技术标签:

【中文标题】为啥这是索引扫描而不是索引查找【英文标题】:why this is an index scan and not an index seek为什么这是索引扫描而不是索引查找 【发布时间】:2017-08-17 09:02:18 【问题描述】:

已在 dw_assesment_detailsdw_assesment_details_id 表上创建聚集索引

/* 6 minutes */
CREATE CLUSTERED INDEX [Ix_DW_ASSESSMENT_DETAILS_qid_QNO_TmpverName] 
ON [dbo].[DW_ASSESSMENT_DETAILS_QUESTION_ID]
(
    [TEMPLATENAME] ASC,
    [TEMPLATEVERSION] ASC,
    [QUESTION_NO] ASC
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, 
      DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO

/* 9 minutes */
CREATE CLUSTERED INDEX [Ix_DW_ASSESSMENT_DETAILS_QNO_TmpverName] 
ON [dbo].[DW_ASSESSMENT_DETAILS]
(
    [TEMPLATENAME] ASC,
    [TEMPLATEVERSION] ASC,
    [QUESTION_NO] ASC
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, 
      DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

SELECT 
    [GETQUESTIONID], 
    dw.[TEMPLATENAME], dw.[TEMPLATEVERSION],
    dw.[QUESTION_NO]
FROM 
    [QIS_DW].[dbo].[DW_ASSESSMENT_DETAILS] dw 
INNER JOIN
    [QIS_DW].[dbo].[DW_ASSESSMENT_DETAILS_QUESTION_ID] id ON dw.TEMPLATENAME = id.TEMPLATENAME 
                                                          AND dw.TEMPLATEVERSION = id.TEMPLATEVERSION 
                                                          AND dw.QUESTION_NO = id.QUESTION_NO

但是上面的选择查询使用的是索引扫描——而不是索引查找。如何改用索引查找?

性能调优专家有什么建议吗?

【问题讨论】:

您正在读取所有数据,并且两个表具有以相同顺序显示数据的索引。您想要索引搜索 您可能想阅读RBarry Young's Answer on this question 【参考方案1】:

如何改用索引查找?

如果您想尝试超越 SQL Server 优化器,您可以指定 LOOP JOIN 查询提示。我希望MERGE JOIN 的计划在多行时表现更好。

请注意,存储引擎还可能在大型扫描期间执行异步预读,以将数据预取到内存中,以便查询可以使用这些数据,而无需等待从存储中读取。返回几行的搜索不会发生预读。

尝试以下查询,看看您的环境是否属于这种情况。

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO

SELECT 
    [GETQUESTIONID], 
    dw.[TEMPLATENAME], dw.[TEMPLATEVERSION],
    dw.[QUESTION_NO]
FROM 
    [QIS_DW].[dbo].[DW_ASSESSMENT_DETAILS] dw 
INNER JOIN
    [QIS_DW].[dbo].[DW_ASSESSMENT_DETAILS_QUESTION_ID] id ON dw.TEMPLATENAME = id.TEMPLATENAME 
                                                          AND dw.TEMPLATEVERSION = id.TEMPLATEVERSION 
                                                          AND dw.QUESTION_NO = id.QUESTION_NO;
GO

SELECT 
    [GETQUESTIONID], 
    dw.[TEMPLATENAME], dw.[TEMPLATEVERSION],
    dw.[QUESTION_NO]
FROM 
    [QIS_DW].[dbo].[DW_ASSESSMENT_DETAILS] dw 
INNER JOIN
    [QIS_DW].[dbo].[DW_ASSESSMENT_DETAILS_QUESTION_ID] id ON dw.TEMPLATENAME = id.TEMPLATENAME 
                                                          AND dw.TEMPLATEVERSION = id.TEMPLATEVERSION 
                                                          AND dw.QUESTION_NO = id.QUESTION_NO
OPTION(LOOP JOIN);
GO

【讨论】:

【参考方案2】:

在 SQL Server 中,聚集或非聚集索引表搜索更改索引扫描,直到我们没有在 Where 子句中添加条件。

Where 子句将使 Index seek 作为执行计划。 如果没有 Where 子句,它充当索引扫描来搜索整个表数据。 这就是为什么索引扫描总是很慢的原因。

【讨论】:

以上是关于为啥这是索引扫描而不是索引查找的主要内容,如果未能解决你的问题,请参考以下文章

将索引扫描转换为索引查找

技术分享 为啥 SELECT 查询选择全表扫描,而不走索引?

将聚簇索引扫描优化为聚簇索引查找

为啥 Spanner 在 LIKE 中使用下划线执行全表扫描,而使用 % 则利用索引?

Postgres:强制分析器使用位图扫描而不是索引扫描

Spring JPA 查询始终使用序列扫描而不是索引扫描