查询选择所有记录时不使用索引

Fir*_*xit 2 sql-server t-sql sql-server-2012

这是我第一次处理大型数据库,我想知道为什么下面的两个查询在执行计划方面是不同的。

我在特定表上设置了索引:

CREATE NONCLUSTERED INDEX [IX_UBHSD_xploc_trandt2] ON [dbo].[UBHSD]
(
    [xploc] ASC, [trandt2] 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) 
        ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

当我运行此查询时,它使用索引。

SELECT COUNT(*) as cnt 
FROM [dbo].[UBHSD] 
WHERE [xploc] != '' AND [trandt2] >= CAST('2012-01-01' AS DATETIME)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

这是一个更大的。

但是当我在不使用聚合的情况下运行查询时:

SELECT *
FROM [dbo].[UBHSD] 
WHERE [xploc] != '' AND [trandt2] >= CAST('2008-01-01' AS DATETIME)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

这是一个更大的。

我期望两个查询都使用索引。但是第二个查询执行全表扫描。

为什么会这样?我错过了什么?

Aar*_*and 5

SELECT *可能使用过which 的列数比xplocand 多trandt2。因此 SQL Server 可以通过遍历索引页轻松得出计数。它无法从那里获取不在索引中的其他列,因此它必须转到表中。

根据表中有多少列(以及许多其他因素),在这种情况下,SQL Server 通常可以做出以下两种选择之一:

  1. 执行查找(在您的情况下看起来像范围扫描),然后执行键/RID 查找以检索索引未涵盖的其他列。
  2. 将其吸起并进行扫描。

在您的情况下,该WHERE子句可能满足足够多的行,因此执行扫描实际上更有效。

一些提高效率的方法:

  1. 停止使用SELECT *- 只需列出您需要的列
  2. 缩减到所需的实际列后,将索引更改为INCLUDE所需实际SELECT列表中的其他列。
  3. 您可以考虑通过添加WHERE子句来过滤索引,具体取决于此确切查询是否是频繁运行的查询(以及 2012 年之前是否有大量数据)。

顺便说一句,为什么这个表上没有聚集索引?