为何WHERE Salary > 0条件未引发表扫描或索引扫描?
问题解答:为什么带
WHERE Salary>0的查询未触发扫描? 先还原你的测试代码:
CREATE DATABASE QueryTest GO USE QueryTest CREATE TABLE Person ( ID INT IDENTITY (1,1), FirstName NVARCHAR(50), SurName NVARCHAR(50), Salary MONEY ) INSERT INTO Person SELECT TOP 2000 FirstName, LastName, RAND(CAST( NEWID() AS varbinary)) *100000 FROM [AdventureWorks2014].[Person].[Person] ORDER BY NEWID() CREATE INDEX IX_Person_Salary ON Person ( Salary )
你的疑问是:执行SELECT Salary FROM Person时触发了表扫描,但SELECT Salary FROM Person WHERE Salary > 0却没有触发表扫描或索引扫描,这是为什么?
核心原因:SQL Server优化器的索引逻辑差异
让我们一步步拆解两个查询的执行计划选择:
1. 无WHERE条件的SELECT Salary FROM Person
你的Person表是一个堆(因为没有显式创建聚集索引,SQL Server默认会将无聚集索引的表作为堆存储)。此时优化器需要在两种读取方式中选最优:
- 选项1:堆扫描(也就是你看到的表扫描):直接读取堆的所有数据页面,提取Salary列。
- 选项2:非聚集索引扫描:读取
IX_Person_Salary的所有索引页面,提取Salary列。
由于你的表只有2000行,堆的页面数量极少,优化器判断堆扫描的IO成本更低,所以选择了表扫描。
2. 带WHERE Salary > 0的查询
这里有两个关键逻辑决定了执行计划的差异:
- 统计信息的判断:SQL Server在创建索引时会生成
Salary列的统计信息,发现你插入的所有Salary值都大于0(RAND()生成0到1之间的随机数,乘以100000后结果必然>0)。这意味着WHERE Salary > 0是一个恒真条件,会匹配表中所有行。 - 覆盖索引与索引seek:你的查询只需要
Salary列,而IX_Person_Salary是一个覆盖索引(查询所需的所有列都包含在索引中)。此时优化器会选择使用索引seek:因为索引是按Salary有序存储的,优化器可以快速定位到Salary>0的起始位置(即索引第一行),然后读取所有后续行。这种操作在执行计划中显示为索引seek,而非扫描——这就是你看到“未引发表扫描或索引扫描”的原因。
简单来说,虽然两个查询最终都返回所有行,但优化器根据查询条件的存在,选择了更高效的索引seek来利用覆盖索引,而非表扫描或索引扫描。
内容的提问来源于stack exchange,提问作者dualcoredba
相关产品推荐
相关产品推荐

