1. 基础数据
RtlBill行数: 54754
RtlDetail行数:4723291
如果不分页,结果集行数:198
2. 常用的分页方式:用 With + Row_Number() 来取分页数据
dbcc dropcleanbuffers;
declare@begin int = 10;
declare@endint= 20;
set statistics io on;
set statistics time on;
;witha as
(
SELECTBillDate,SUM([QTY]) PosInQty,row_number() over(order by billdate) as id
FROMRtlDetail Ainner join RtlBill B on (A.billno = B.billno)
GROUP BY BillDate
)
select* from a whereid>@begin andid<=@end;
set statistics io off;
set statistics time off;
(10 行受影响)
表 'RtlBill'。扫描计数 17,逻辑读取 496 次,物理读取 1 次,预读 165 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'RtlDetail'。扫描计数 17,逻辑读取 49888 次,物理读取 3 次,预读 49284 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
sql Server 执行时间:
cpu 时间 = 4767 毫秒,占用时间 = 5880 毫秒。
从IO及cpu时间可以看出,这个分页sql消耗资源多,耗时长,下面通过执行计划来分析一下原因,寻找优化方法;
2. 执行计划分析
(1)从执行计划中,可以看到大量使用并行处理;
(2)红色圈出的“哈希匹配”部分,详情,如下图,其中可以看到,实际行数为198行,也就是未分页的完整结果的行数,为什么sqlServer不能在取到30行后就停止执行,直接返回呢?
(3)接着看红色圈出的“并行度”部分,详情如下图。sqlServer在进行多线程处理的时候,使用的任务分配方法,一般为Hash,或RoundRobin方式。Hash方式即对数据做Hash计算,来确定究竟传给哪个线程处理,RoundRobin为轮询方式。每个线程之间没有消息通讯。这样导致,在所有线程完成全部处理之前,系统无法获知究竟是否取到了所要获取的分页中的所有数据。那么,如果我们使用单线程的方式,并且在With语句内部就用TOP的方式告诉sqlServer,我只要获取@end条记录就可以了,是否就可以让sqlServer做出正确的相应呢?
注意:这里需要用sql拼接的方式直接把行数拼接进语句中,否则,生成执行计划时,sql无法预估结果集。
呵呵,关于参数的问题,单独一篇来分析。
dbcc dropcleanbuffers;
set statistics io on;
set statistics time on;
;witha as
(
SELECT Top 20 BillDate,row_number() over(order bybilldate) as id
FROMRtlDetail Ainner join RtlBill B on (A.billno = B.billno)
GROUP BY BillDate
)
select* from a whereid>10 option(maxdop 1);
set statistics io off;
set statistics time off;
4. 实验结果对比
5. 不适用的情况
(1)使用 Dense_Rank()等函数进行分页时,不能使用TOP方式(因为,结果行数都不同);
(2)需要大量计算的sql(这个需要实际分析,如果cpu耗时太多,单线程处理的优势就体现不出来了)。
6. 优化策略
尝试Top+SingleThread的方式。有效?OK
折中一下,单独使用Top方式
尝试,用count(*) Over()优化首页
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。