我有一个查询,其中使用select *
不仅读取次数少得多,而且比使用占用的CPU时间少得多select c.Foo
。
这是查询:
select top 1000 c.ID
from ATable a
join BTable b on b.OrderKey = a.OrderKey and b.ClientId = a.ClientId
join CTable c on c.OrderId = b.OrderId and c.ShipKey = a.ShipKey
where (a.NextAnalysisDate is null or a.NextAnalysisDate < @dateCutOff)
and b.IsVoided = 0
and c.ComplianceStatus in (3, 5)
and c.ShipmentStatus in (1, 5, 6)
order by a.LastAnalyzedDate
这完成了2,473,658个逻辑读取,大部分在表B中。它使用了26,562个CPU,持续时间为7,965。
这是生成的查询计划:
在PasteThePlan上:https ://www.brentozar.com/pastetheplan/ ? id = BJAp2mQIQ
当我改变c.ID
到*
,查询与完成107049逻辑读,非常均匀地分布所有三个表之间。它使用4,266 CPU,持续时间为1,147。
这是生成的查询计划:
在PasteThePlan上:https ://www.brentozar.com/pastetheplan/ ? id = SyZYn7QUQ
我试图用乔Obbish建议的查询提示,与这些结果:
select c.ID
无提示:https://www.brentozar.com/pastetheplan/?id=SJfBdOELm
select c.ID
与提示:https://www.brentozar.com/pastetheplan/ ?id = B1W ___ N87
select *
无提示:https : //www.brentozar.com/pastetheplan/?id=HJ6qddEIm
select *
有提示:https : //www.brentozar.com/pastetheplan/?id=rJhhudNIQ
OPTION(LOOP JOIN)
与select c.ID
没有提示的版本相比,使用带有with 的提示确实极大地减少了读取次数,但仍比select *
没有任何提示的查询读取次数多了约4倍。添加OPTION(RECOMPILE, HASH JOIN)
到select *
查询中使其性能比我尝试过的任何其他方法都要差得多。
在使用更新表及其索引的统计信息之后WITH FULLSCAN
,select c.ID
查询的运行速度更快:
select c.ID
更新之前:https : //www.brentozar.com/pastetheplan/?id=
select *
SkiYoOEUm更新之前:https : //www.brentozar.com/ ?pastetheplan / ID = ryrvodEUX
select c.ID
更新后:https://www.brentozar.com/pastetheplan/?id=B1MRoO487
select *
更新后:https://www.brentozar.com/pastetheplan/?id=Hk7si_V8m
select *
select c.ID
在总持续时间和总读取次数方面仍然优于(select *
大约是读取次数的一半),但是它确实使用更多的CPU。总体而言,它们比更新之前要紧密得多,但是计划仍然不同。
在以2014 Compatibility模式运行的2016和2014上可以看到相同的行为。什么可以解释这两个计划之间的差异?可能是因为尚未创建“正确的”索引吗?统计信息可能会稍微过时导致此吗?
我尝试ON
以多种方式将谓词上移到连接的一部分,但是每次查询计划都是相同的。
重建索引后
我在查询所涉及的三个表上重建了所有索引。 c.ID
仍然执行最多的读取(是的两倍*
),但是CPU使用率约为该*
版本的一半。该c.ID
版本还蔓延到tempdb数据库上的排序ATable
:
c.ID
:https://www.brentozar.com/pastetheplan/?id=HyHIeDO87
*
:https://www.brentozar.com/pastetheplan/?id=rJ4deDOIQ
我还尝试强制它在没有并行性的情况下运行,这给了我最佳性能的查询:https : //www.brentozar.com/pastetheplan/?id=SJn9-vuLX
我注意到在执行大索引查找之后,在单线程版本中仅执行了1000次排序的操作符的执行计数,但是在并行版本中执行的排序却要多得多,介于各种操作符的2,622至4,315次执行之间。
select c.ID
查询快得多,但它仍在做一些select *
没有提示的查询所要做的额外工作。