我有一个查询,其中使用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 *没有提示的查询所要做的额外工作。