我创建了一个示例表,如下所示
CREATE TABLE [dbo].[StatisticsDemo](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL
) ON [PRIMARY]
然后我插入下面的数据如下:
SELECT NAME,COUNT(*) AS COUNT
FROM StatisticsDemo
GROUP BY NAME
NAME COUNT
-------------
AABBCC 59999
XXYYZZ 1
然后我在非聚集索引下创建:
CREATE NONCLUSTERED INDEX [NCI_STATISTICSDEMO_NAME] ON [dbo].[StatisticsDemo]
(
[Name] ASC
)
现在我运行了以下查询:
SELECT NAME FROM [dbo].[StatisticsDemo]
WHERE NAME = 'AABBCC'
正如预期的那样,它返回 59999 行,但它正在对非聚集索引进行索引查找。但据我所知,它应该进行索引扫描,因为 99.99% 的数据满足选择查询中提到的过滤条件。
有人能告诉我为什么它在做索引搜索而不是索引扫描吗?
整个活动的目的是证明(因为我将要介绍统计数据)SQL Server 在准备执行计划之前并根据记录匹配表中的总记录,它将决定进行扫描或搜索。如果记录匹配的百分比大约等于表中的记录总数,则应该进行 SCAN。但这并没有发生。当我使用 AdventureWorks2016 数据库并运行以下查询时也是如此:
select * from [Sales].[SalesOrderHeader] WHERE SalesOrderID >= 43659 AND
SalesOrderID <= 73659
上面的查询返回 31465 条记录中的 30001 条记录。但它仍在执行 Clustered Index Seek。
我变得非常困惑,它正在动摇我的概念。:( 可以请一些帮助。
PS:我也清理了计划缓存,但没有运气。SQL Server 版本是 2016。
这是不正确的,所以解释了为什么你没有看到它。
BETWEEN 43659 AND 73659
范围搜索正在执行部分扫描。它只能使用 B-tree 来寻找开始扫描的点(因此避免读取任何低于 的内容43659
),如果存在值大于 的行,则可能提前退出73659
。对于范围内的行,它只是读取页面并按照与索引排序扫描完全相同的方式跟随链接列表到下一个叶子页面。
没有理由要在这里进行扫描。充其量它节省了一些逻辑读取,用于从根导航到叶以找到起点,但代价是读取超出范围的额外行。
您的理解是不正确的,我相信您将您的案例与引爆点问题混淆了,这是一个值得阅读和理解的问题。
在SQL Server中,查找是一种使用 B 树开始或停止读取特定值的行的操作。从理论上讲,您可以按照扫描的确切顺序进行搜索读取每一行。
对于 Oracle 和其他 RDMS,此操作(在定义的开始和停止值处读取多行)称为范围扫描。
这是读取每一行的 seek 的快速演示
请记住,一次查找可以读取多行。在这个演示中,它读取的数量与扫描的数量完全相同。