考虑这个SQL性能差异,在第一个中,我选择所有26,000行,而在第二个中,我只想要前5个。
SELECT tw.*
FROM entity e
JOIN entity_tag et on et.entity_id = e.id
JOIN tag t on t.tag_id = et.tag_id
JOIN tagrelatedtweets trt on trt.FK_Tag_ID = t.tag_id
JOIN tweets tw on tw.PK_Tweet_ID = trt.FK_Tweet_ID
WHERE e.id = 765131
ORDER BY tw.[timestamp]
VS
SELECT TOP (5) tw.*
FROM entity e
JOIN entity_tag et on et.entity_id = e.id
JOIN tag t on t.tag_id = et.tag_id
JOIN tagrelatedtweets trt on trt.FK_Tag_ID = t.tag_id
JOIN tweets tw on tw.PK_Tweet_ID = trt.FK_Tweet_ID
WHERE e.id = 765131
ORDER BY tw.[timestamp]
Without: CPU = 201 | Reads: 6880 | Writes: 0 | Duration: 451
With: CPU = 302439 | Reads: 7453199 | Writes: 3169 | Duration: 74188
这对我来说没有意义......还有其他方法可以解决这个问题吗?
在Martin提出所有相关表格的REBUILD统计数据之后,有一个小的改进,但将TOP数量更改为参数的技巧效果最好。
统计重建之前:
CPU = 302439 | Reads: 7453199 | Writes: 3169 | Duration: 74188
统计重建后:
CPU = 127734 | Reads: 4100436 | Writes: 2656 | Duration: 16880
使用参数:
CPU = 218 | Reads: 6899 | Writes: 0 | Duration: 83
使用参数查询:
DECLARE @TOP INT; SET @TOP=5;
SELECT TOP (@TOP) tw.*
FROM entity e
JOIN entity_tag et on et.entity_id = e.id
JOIN tag t on t.tag_id = et.tag_id
JOIN tagrelatedtweets trt on trt.FK_Tag_ID = t.tag_id
JOIN tweets tw on tw.PK_Tweet_ID = trt.FK_Tweet_ID
WHERE e.id = 765131
ORDER BY tw.timestamp desc
对于那些使用Entity Framework的人来说,最后一句话;如果您遇到此行为,您可以模拟相同的基于参数的行为,如下所示:
.Take(100).ToList().Take(5)
我知道它并不漂亮,但如果你使用的是实体框架,这是触发正确执行计划的唯一方法。
非常感谢你指出我正确的方向马丁!
答案 0 :(得分:3)
在评论中讨论之后,似乎由于某种原因,SQL Server选择针对“仅前5行”情况进行优化的计划是次优的。它可能会使用非阻塞运算符,例如嵌套循环连接,而不是阻塞散列连接等。
由于您无法发布执行计划,因此很难确切地知道原因,但是比较查询的两个版本的计划并查看问题查询的实际执行计划中的实际行数与估计行数对这件事情有所了解。
在这种情况下,似乎在编译时隐藏优化器中的TOP 5
信息就足以为您提供所需的计划了!