关于SQL执行计划中Sort运算符高成本及行数估算异常的技术咨询
聊聊你的执行计划疑问
咱们来一步步拆解你遇到的这些问题,结合你的查询语句分析就清楚了:
先贴下你的查询方便参考:
DECLARE @PageIndex INT = 1000; DECLARE @PageCount INT = 1000; SELECT ID FROM dbo.Table1 WHERE DateCreated >= '2021-10-27' AND DateCreated < '2021-10-28' ORDER BY ID OFFSET @PageIndex * @PageCount ROWS FETCH NEXT @PageCount ROWS ONLY
1. 为啥Sort运算符成本比读190万行的Index Seek还高?
SQL Server的执行计划成本可不是按行数多少来拍脑袋算的,它是基于实际资源消耗模型来估算的:
- 你的Index Seek如果是走
DateCreated上的索引,读取190万行其实是很高效的——毕竟索引是有序存储的,属于顺序IO,成本被估算得很低。 - 但Sort就不一样了:你这查询要
OFFSET 1000000 ROWS,意味着SQL Server得先把所有符合日期条件的190万行全量排序,然后再跳过前100万行取后面的1000行。Sort的成本主要看内存够不够用:如果排序的数据量超过了SQL Server分配的内存配额,就会触发磁盘溢出排序——把数据写到tempdb的磁盘上,这可比内存排序慢太多了,CPU和IO成本直接飙升。哪怕执行计划显示“仅排序100行”,那也是指Sort最终输出的行数,不是它实际处理的190万行,这才是成本高的核心原因。
2. 为啥Index Seek返回190万行,却只有100行传到Sort?
这大概率是你对执行计划的解读偏差了:
- 执行计划里运算符的“每执行估算行数”,对于Sort来说,显示的是它最终输出到下一个步骤的行数(也就是你FETCH的1000行,你说的100可能是估算错误,或者看错数字啦),而不是它接收的输入行数。你可以看看运算符之间的箭头粗细——从Index Seek到Sort的箭头应该是很粗的,对应实际传递的190万行。
- 另外,这么大的偏移量(100万),SQL Server没法用Top N Sort优化(那种只排序前N行的操作),必须先全量排序所有符合条件的行,所以Sort的输入肯定是190万行,没跑。
3. Sort的每执行估算行数为啥只有100?
这是基数估算偏差搞的鬼:
- 你用了变量
@PageIndex和@PageCount,SQL Server在编译查询的时候不知道这些变量的具体值,只能用默认的基数规则来估算,结果就不准了——它可能错误估算了OFFSET后的输出行数。 - 你可以试试把变量换成常量(比如直接写
OFFSET 1000000 ROWS)再看执行计划,这时候基数估算会准确很多,Sort的输出行数应该会显示1000,而不是100。
给你个优化小建议
这种大偏移量的分页查询性能本来就差,因为要全量排序。你可以试试创建一个按ID排序的覆盖索引:
CREATE NONCLUSTERED INDEX IX_Table1_ID_DateCreated ON dbo.Table1 (ID) INCLUDE (DateCreated)
然后改写查询,先找到符合日期条件的ID范围,再利用ID的有序性来分页,这样就能避免全量排序,性能会提升很多。
内容的提问来源于stack exchange,提问作者Evan Payne
相关产品推荐
相关产品推荐

