Sql server SQLServer并没有选择使用索引,尽管一切似乎都表明了这一点

Sql server SQLServer并没有选择使用索引,尽管一切似乎都表明了这一点,sql-server,tsql,Sql Server,Tsql,这里出了点问题,我不明白是什么。值得一提的是,表中没有搜索到的值,对于现有值来说没有问题。但是,为什么第一个查询需要对主键进行聚集键搜索,而第二个查询甚至没有在查询中使用,而第二个查询可以直接在索引上运行呢。 强制查询使用带有(index(indexname))的索引确实有效,但为什么优化器不选择单独使用它呢 列PIECE_NUM不在任何其他索引中,也不是主键 打开统计信息IO 声明@vchEventNum VARCHAR(50) 设置@vchEventNum='54235DDS28KC1F5S

这里出了点问题,我不明白是什么。值得一提的是,表中没有搜索到的值,对于现有值来说没有问题。但是,为什么第一个查询需要对主键进行聚集键搜索,而第二个查询甚至没有在查询中使用,而第二个查询可以直接在索引上运行呢。 强制查询使用带有(index(indexname))的索引确实有效,但为什么优化器不选择单独使用它呢

列PIECE_NUM不在任何其他索引中,也不是主键

打开统计信息IO
声明@vchEventNum VARCHAR(50)
设置@vchEventNum='54235DDS28KC1F5SJQMWZ'
选择前1名
重量,
fwt试验结果
来自FIN_重量试验fwt(无锁)
其中fwt.PIECE_NUM类似于@vchEventNum+“%”
按fwt.DTTM\U插入说明订购
选择前1名
重量,
fwt试验结果
来自FIN_重量试验fwt(无锁)
其中fwt.PIECE_NUM类似于“54235DDS28KC1F5SJQMWZ”+“%”
按fwt.DTTM\U插入说明订购
关闭统计信息
我让两个查询在一批中运行:

IO统计报告:

查询1:逻辑读取16244910

查询2:逻辑读取5

Table 'FIN_WEIGHT_TESTS'. Scan count 1, logical reads 16244910, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'FIN_WEIGHT_TESTS'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
该表在PIECE_NUM上有一个非聚集索引,包括查询中的所有其他三列

以下是查询执行计划(稍加编辑即可删除实际名称):

我注意到convert_是隐式的,但这只是因为将varchar参数转换为nvarchar列。更改参数类型不会更改查询的行为


为什么带参数的查询在用其值替换参数时不使用索引?

将扫描第一个查询,因为您使用的是局部变量。优化器将其视为“匿名”值,因此无法使用统计信息构建良好的查询计划

第二个查询之所以搜索,是因为它是一个文本值,SQL可以查看它的统计数据,并且更清楚地知道它将找到多少使用该值的估计行

如果您按照以下方式运行第一个查询,我想您会看到它使用了更好的计划:

DECLARE  @vchEventNum   VARCHAR(50)
SET  @vchEventNum = '54235DDS28KC1F5SJQMWZ'

SELECT TOP 1 
        fwt.WEIGHT,
        fwt.TEST_RESULT
FROM FIN_WEIGHT_TESTS fwt WITH(NOLOCK)
WHERE fwt.PIECE_NUM LIKE @vchEventNum  + '%'
ORDER BY fwt.DTTM_INSERT DESC
OPTION(RECOMPILE)

我建议使用参数化过程来运行此代码,以确保它使用缓存的计划。使用重新编译提示有其自身的缺点,因为优化器每次运行时都需要重新生成计划。因此,如果您经常运行此代码,我将避免此提示


您可以在这里阅读有关局部变量的内容:

我认为发生这种情况的原因是在查询中使用了
@variable
ORDER BY
。要测试我的猜测,请从查询中删除
order by
,这可能会导致两种情况下的计划相等(这一次,select中报告的估计行数不同)

如前一个答案中所述,由于批处理被视为一个整体,因此无法在编译时嗅探局部变量,并且只有
recompile
选项允许服务器在编译时知道变量的值,因为当变量已分配时,重新编译开始。在第一种情况下,这会导致“估计未知”,即由于我们不知道过滤器中的值,因此无法使用统计信息,因此会估计输出中的更多行

但是查询中有
top+orderby
。这意味着,如果我们希望得到许多行,那么除了第一行之外,只有一行按
DTTM\u INSERT DESC
排序,我们必须
sort
对所有过滤的行进行排序。事实上,如果您查看第二个计划,您会发现
SORT
操作符的成本最高。但是当您使用常量时,SQLServer使用统计信息并发现只返回一行,因此它可以允许对结果进行排序


如果需要许多行,它决定使用已由
DTTM\u INSERT
排序的索引。这只是我的猜测,因为您没有在这里发布索引的创建脚本,但从计划中我看到,第一个计划肯定会转到聚集索引以获取非聚集索引中缺少的字段,这意味着它与第二种情况中使用的非聚集索引不同,但我确信在第一种情况下选择的索引有
前导键列DTTM\u INSERT
。这样做,服务器消除了我们在第二个计划中看到的
排序

1-请不要随意使用nolock:2-了解参数嗅探:@Milney感谢链接,将阅读更多关于nolock的信息,因为它在我们的数据库中被大量使用。这里不是我的代码,我只是想弄清楚为什么这个查询每天会创建数十亿次读取。还将阅读有关参数嗅探的内容。在这里,我认为所有提示都有,优化器可以知道该做什么-只有一个条件列,该列所在的位置只有一个索引。关于变量,这是可以的,但仍然没有解释为什么在非聚集的字段中有所有请求的字段时选择聚集+非聚集“建议使用参数化过程”-不知道你的意思。代码位于存储过程中,但是@vchEventNum是SP中的局部变量。当使用局部变量时,除非使用重新编译选项,否则无法嗅探该变量。如果改为使用@vchEventNum作为sp的参数,则在第一个过程编译时会对其进行嗅探,并且将为该嗅探值生成计划,估计将更准确,并且将选择第二个计划。我想强制查询使用正确的索引可能是比重新编译更好的解决方案。@sepupic好,谢谢,现在已经很清楚了,但不能将变量中的值作为参数提供给SP。因此我将继续使用(index())。谢谢,您必须第三次阅读您的帖子才能获得所有内容,但您是对的,通过删除订单消除了扫描和查询计划