Sql 为什么薪酬>;0未导致表/索引扫描

Sql 为什么薪酬>;0未导致表/索引扫描,sql,sql-server-2014,sql-execution-plan,Sql,Sql Server 2014,Sql Execution Plan,我有一个用于此练习的测试表: 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,

我有一个用于此练习的测试表:

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 > 270
但是,如果我这样做:

SELECT Salary FROM Person WHERE Salary > 0
我得到一个索引查找(尽管它返回表中的所有行)

再者,如果我跑

SELECT Salary FROM Person
SELECT Salary FROM Person WHERE Salary > 0
在同一批次中,它们都是该批次的50%

这是怎么回事? 如果返回所有行,当WHERE子句(如果存在)时,为什么SQL Server使用seek

为什么索引查找与索引扫描的成本相同


我的印象是,SQL Server将使用其统计信息来估计要返回的行数,然后相应地计划其执行。统计信息将告诉它,所有行都是>0,因此在这种情况下,扫描的成本较低?

查询仅检索一列
Person

SELECT Salary FROM Person WHERE Salary > 0 
同时,只有一个条件
Salary>0
使用同一列
Person



如果salary列上有一个索引,那么只扫描整个表上的该索引会更便宜。对于此查询,这就是所谓的索引,因为该索引包含执行此查询所需的所有信息,数据库会从索引文件中读取所有需要的信息,而不会到达该表。

n此处:

首先:没有表扫描,因为您的数据位于非聚集索引中(可以将其视为仅包含薪水的表的较小有序副本),因此从索引中获取所需的所有数据会更快

其次:>0的东西和性能分割

[Salary]的列定义允许空值。当SQL生成执行计划时,它假定表中可能有空值,因此无法明确预测>0将返回所有值。SQL“计划”执行搜索,但最终“实际”执行扫描。实际执行计划是估计的执行计划,但带有附加指标

下面的代码演示在我的环境中以52%48%的比例显示了这种行为

CREATE TABLE #TMP1
(
    ID INT IDENTITY (1,1),
    FirstName   NVARCHAR(50),
    SurName     NVARCHAR(50),
    Salary      MONEY
)

CREATE TABLE #TMP2
(
    ID INT IDENTITY (1,1),
    FirstName   NVARCHAR(50),
    SurName     NVARCHAR(50),
    Salary      MONEY NOT NULL
)
GO

INSERT INTO #TMP1
SELECT  'xxxxx','xxxxx',RAND(CAST( NEWID() AS varbinary)) *100000
GO 2000


INSERT INTO #TMP2
SELECT FirstName,SurName,Salary FROM #TMP1


CREATE INDEX IX_Person_Salary ON #TMP1
(
    Salary
)
CREATE INDEX IX_Person_Salary ON #TMP2
(
    Salary
)


SELECT Salary FROM #TMP1 WHERE Salary > 0
SELECT Salary FROM #TMP2 WHERE Salary > 0
更新


检查你的索引的直方图,如果它从0开始,那么你需要做>=0才能得到完整的扫描。

我问这个问题已经好几年了,但我想我现在可以回答我自己的问题了。(我还回读了一些对我来说更有意义的其他答案,所以如果我在下面重复,我很抱歉)

首先,我认为在我的问题中有一个术语的拼写错误/误用:

如果我运行以下命令,我将得到一个表扫描 期待

应该读

我得到了一个非聚集索引扫描,这是我所期望的

这是因为作为查询

SELECT Salary FROM Person
将扫描最窄的相关索引,在本例中为
IX_Person_Salary

按照扫描方式,我们从索引的一端开始读取(在这种情况下,读取到末尾)

下面的查询生成一个索引查找
从薪资>270的人员中选择薪资

正如上面文章中提到的,seek基本上是对索引的扫描,我们知道从哪一行开始扫描,当值不再与谓词匹配时,它将停止扫描(这可能是索引的一部分,也可能是一直到最后)
WHERE
子句意味着我们从
Salary=270
开始扫描该索引,并从中读取大于270的所有值(即所有值)。如果我们在
WHERE
子句中还有一个
和Salary
,则一旦我们在索引中点击
n
,seek将停止读取

从薪资>0的人员中选择薪资
也会导致搜索,但搜索实际上是完全索引扫描,因为它将从Salary>0开始扫描Salary>0的所有值(即所有值)事实上,这与
SELECT Salary FROM Person
查询上的非聚集索引扫描相同,这可以通过在两个查询上读取的实际行数在各自的计划中相同来验证


由于
SELECT Salary FROM Person(其中Salary>0
SELECT Salary FROM Person
上的估计行数(以及因此产生的成本)相同,因此这两个计划都将花费50%(因为计划的成本是估计成本,即使在实际计划中也是如此)

您是否在执行之间清除缓冲区和计划缓存?可能您启用了。我认为这是意料之中的,因为引擎仍然需要比较salary的值,以查看它是否大于零,就像将它与2700进行比较时一样。它仍然是一个seek,即使它返回所有内容,因为它仍然需要解析Index.由于seek只涉及符合条件的行,并解析包含这些行的所有行,因此成本与符合条件的行数和解析数成正比,而不是与表中的总行数成正比。即使每次都被提取,情况也是如此。除非我完全偏离基准。我以为SQL Server会使用其statistics估计要返回多少行,然后相应地计划执行。统计信息会告诉它,所有行都大于0,因此在这种情况下,扫描的成本会更低?如果有100k行和新更新的统计信息,这会有区别吗?我还希望对覆盖索引进行索引扫描,但是OP正在看到索引搜索。仍然显示搜索