为什么这是一个索引扫描而不是索引寻求?

RPM*_*984 16 t-sql indexing performance sql-server-2008 sql-execution-plan

这是查询:

SELECT      top 100 a.LocationId, b.SearchQuery, b.SearchRank
FROM        dbo.Locations a
INNER JOIN  dbo.LocationCache b ON a.LocationId = b.LocationId
WHERE       a.CountryId = 2
AND         a.Type = 7
Run Code Online (Sandbox Code Playgroud)

位置索引:

PK_Locations:

LocationId

IX_Locations_CountryId_Type:

CountryId,Type

LocationCache索引:

PK_LocationCache:

LocationId

IX_LocationCache_LocationId_SearchQuery_SearchRank:

LocationId,SearchQuery,SearchRank

执行计划:

在此输入图像描述

所以它正在使用覆盖索引做一个关于位置的索引搜索,很酷.

但是为什么它在LocationCache覆盖索引上进行索引扫描

覆盖索引在索引中具有LocationId,SearchQuery,SearchRank(而不是"包含的列").

将鼠标悬停在索引扫描上:

在此输入图像描述

此查询需要进入由自动完成插件使用的SQL Server FTS目录提供的索引视图,因此需要100%优化.

目前,上述查询需要3秒钟.它应该<0.

有任何想法吗?

RBa*_*ung 33

它主要使用索引扫描,因为它也使用合并连接.Merge Join运算符需要两个输入流,这两个输入流都按照与Join条件兼容的顺序排序.

它正在使用Merge Join运算符来实现INNER JOIN,因为它相信它会比更典型的Nested Loop Join运算符更快.并且它可能是正确的(通常是),通过使用它选择的两个索引,它具有根据您的连接条件(LocationID)预先排序的输入流.当输入流像这样预先排序时,Merge Joins几乎总是比其他两个(Loop和Hash Joins)更快.

缺点是你已经注意到了:它似乎是在扫描整个索引,所以如果读取那么多可能永远不会被使用的记录,那怎么会更快呢?答案是,扫描(因为它们的顺序性质)可以读取任何数量的记录/秒的10到100倍.

现在寻求通常赢,因为它们是有选择性的:它们只获得你要求的行,而扫描是非选择性的:它们必须返回范围内的每一行.但由于扫描有很多更高的读取速度,他们可以经常击败只要废弃行来匹配行的比例为寻求更低比扫描行/秒VS的比 寻求行/秒.

有问题吗?


好的,我被要求更多地解释最后一句话:

"Discarded Row"是Scan读取的一个(因为它必须读取索引中的所有内容),但是Merge Join操作符会拒绝它,因为它在另一侧没有匹配,可能是因为WHERE子句条件已经排除它.

"匹配行"是它读取的实际上与Merge Join中的某些内容匹配的行.如果扫描被Seek替换,这些行将被Seek读取.

您可以通过查看查询计划中的统计信息来确定存在的内容.看到Index Scan左侧那个巨大的胖箭头?这表示优化器认为它将使用Scan读取的行数.您发布的索引扫描的统计信息框显示返回的实际行数约为5.4M(5,394,402).这相当于:

TotalScanRows = (MatchingRows + DiscardedRows)
Run Code Online (Sandbox Code Playgroud)

(就我而言,无论如何).要获得匹配行,请查看Merge Join运算符报告的"实际行"(您可能必须取下TOP 100才能准确地获得此值).一旦你知道这一点,你可以通过以下方式获得Discarded行:

DiscardedRows = (TotalScanRows - MatchingRows)
Run Code Online (Sandbox Code Playgroud)

现在你可以计算比率了.

  • +1这是一个非常有用的答案。查看我的数字后,它正在对 400,000 行执行_索引扫描_,但仅匹配_散列连接_中的 201 行。因此,根据比率,我猜测 _Loop Join_ 会更有效。 (2认同)

Wil*_*l A 8

同时请记住,当对其进行其他更改时,它将导致查询可能执行得很糟糕,使用INNER LOOP JOIN应强制使用覆盖索引dbo.LocationCache.

SELECT      top 100 a.LocationId, b.SearchQuery, b.SearchRank
FROM        dbo.Locations a
INNER LOOP JOIN dbo.LocationCache b ON a.LocationId = b.LocationId
WHERE       a.CountryId = 2
AND         a.Type = 7
Run Code Online (Sandbox Code Playgroud)

  • 另一只小猫刚刚去世:〜( (3认同)