Sql server 改进报告存储过程执行时间-调整临时表?
我的任务是提高由SSRS前端调用的报告存储过程的性能(这是我第一次在现实世界中进行性能调优),该存储过程目前在最大数据量(基于从报告前端设置的过滤器)上运行大约需要30秒 此存储过程中执行了19个查询,其中大多数查询将数据从基表内部的初始(遗留)格式转换为有意义的数据集,以显示给业务端 我基于几个DMV创建了一个查询,以找出存储过程中哪些是最消耗资源的查询(下面是一个小片段),我找到了一个平均需要10秒才能完成的查询Sql server 改进报告存储过程执行时间-调整临时表?,sql-server,performance,stored-procedures,sql-server-2008-r2,Sql Server,Performance,Stored Procedures,Sql Server 2008 R2,我的任务是提高由SSRS前端调用的报告存储过程的性能(这是我第一次在现实世界中进行性能调优),该存储过程目前在最大数据量(基于从报告前端设置的过滤器)上运行大约需要30秒 此存储过程中执行了19个查询,其中大多数查询将数据从基表内部的初始(遗留)格式转换为有意义的数据集,以显示给业务端 我基于几个DMV创建了一个查询,以找出存储过程中哪些是最消耗资源的查询(下面是一个小片段),我找到了一个平均需要10秒才能完成的查询 select object_name(st.objectid)
select
object_name(st.objectid) [Procedure Name]
, dense_rank() over (partition by st.objectid order by qs.last_elapsed_time desc) [rank-execution time]
, dense_rank() over (partition by st.objectid order by qs.last_logical_reads desc) [rank-logical reads]
, dense_rank() over (partition by st.objectid order by qs.last_worker_time desc) [rank-worker (CPU) time]
, dense_rank() over (partition by st.objectid order by qs.last_logical_writes desc) [rank-logical write]
...
from sys.dm_exec_query_stats as qs
cross apply sys.dm_exec_sql_text (qs.sql_handle) as st
cross apply sys.dm_exec_text_query_plan (qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) as qp
where st.objectid in ( object_id('SuperDooperReportingProcedure') )
, [rank-execution time]
, [rank-logical reads]
, [rank-worker (CPU) time]
, [rank-logical write] desc
现在,这个查询有点奇怪,因为执行计划显示,大部分工作(~80%)是在将数据插入本地临时表时完成的,而不是在查询从中获取源数据并进行操作的其他表时完成的。(下面的屏幕截图来自SQL Sentry Plan Explorer)
此外,就行估计而言,执行计划对此有很大的估计,因为只有4218行插入到本地临时表中,而不是执行计划认为其正在移动到本地临时表中的248k行。因此,由于这个原因,我在考虑“统计”,但即使约80%的工作是实际插入到表中,这些数据是否仍然重要
我的第一个建议是重新编写整个过程和存储过程,以便不将数据的移动和转换包括到报告存储过程中,并每晚将数据转换到一些持久化表中(不需要实时数据,只需要在前一天结束前的相关数据)。但业务方面不想投入时间和资源进行重新设计,而是“建议”我进行性能调整,以找到我可以在何处添加哪些索引来加速这一过程
我不相信向基表添加索引会提高报告的性能,因为运行查询所需的大部分时间都是将数据保存到临时表中(据我所知,这会影响tempdb,这意味着它们将被写入磁盘->由于I/O延迟而增加的时间)
但是,即使如此,正如我所提到的,这是我的第一个性能调整任务,我在过去几天里尽可能多地阅读了与此相关的内容,这些是我到目前为止的结论,但我想征求更广泛的听众的意见,并希望获得更多的见解和理解,我可以做些什么来改进这个过程
如能回答以下几个明确的问题,我将不胜感激:
- 我上面所说的(我对db的理解或我的假设)是否有任何错误
- 向临时表添加索引实际上会增加执行时间,因为每次执行时都会重建表(及其关联的索引),这是真的吗
- 在这种情况下,是否可以不必重新编写过程/查询,只通过索引或其他调优方法来执行其他操作?(我读过一些文章标题,你也可以“调优tempdb”,但我还没有深入了解这些内容的细节)
GROUP BY
部分中的几个聚合列及其对应行:
select
b.ProgramName
,b.Region
,case when b.AM IS null and b.ProgramName IS not null
then 'Unassigned'
else b.AM
end as AM
,rtrim(ltrim(b.Store)) Store
,trd.Store_ID
,b.appliesToPeriod
,isnull(trd.countLeadActual,0) as Actual
,isnull(sum(case when b.budgetType = 0 and b.budgetMonth between @start_date and @end_date then b.budgetValue else 0 end),0) as Budget
,isnull(sum(case when b.budgetType = 0 and b.budgetMonth between @start_date and @end_date and (trd.considerMe = -1 or b.StoreID < 0) then b.budgetValue else 0 end),0) as CleanBudget
...
into #SalvesVsBudgets
from #StoresBudgets b
left join #temp_report_data trd on trd.store_ID = b.StoreID and trd.newSourceID = b.ProgramID
where (b.StoreDivision is not null or (b.StoreDivision is null and b.ProgramName = 'NewProgram'))
group by
b.ProgramName
,b.Region
,case when b.AM IS null and b.ProgramName IS not null
then 'Unassigned'
else b.AM
end
,rtrim(ltrim(b.Store))
,trd.Store_ID
,b.appliesToPeriod
,isnull(trd.countLeadActual,0)
选择
b、 程序名
,b.区域
,b.AM为null且b.ProgramName不为null时的大小写
然后是“未分配”
其他早餐
以AM结束
,rtrim(ltrim(b.Store))商店
,trd.Store\u ID
,b.appliesToPeriod
,isnull(trd.countLeadActual,0)为实际值
,isnull(总和(当b.budgetType=0且b.budgetMonth介于@start_date和@end_date之间,然后b.budgetValue else 0 end时的情况),0)作为预算
,isnull(当b.budgetType=0且b.budgetMonth介于@start_date和@end_date之间时,以及(trd.considerMe=-1或b.StoreID<0)和b.budgetValue else 0 end时,总和为0)作为CleanBudget
...
进入#救助计划
来自#StoresBudgets b
在trd.store\u ID=b.StoreID和trd.newSourceID=b.ProgramID上左连接#temp_report_data trd
其中(b.StoreDivision不为null或(b.StoreDivision为null且b.ProgramName='NewProgram'))
分组
b、 程序名
,b.区域
,b.AM为null且b.ProgramName不为null时的大小写
然后是“未分配”
其他早餐
结束
,rtrim(ltrim(b.Store))
,trd.Store\u ID
,b.appliesToPeriod
,isnull(trd.countLeadActual,0)
我不确定这是否真的有用,但由于@kcung请求了它,我添加了信息
另外,要回答他的一些问题:
- 临时表上没有索引
- 内存大小:32 GB
CASE
语句从聚合生成查询中移出,但不幸的是,总体而言,程序时间没有明显改善,因为它仍然在±0.25到±1.0秒的范围内波动(是的,与原始版本的存储过程相比,时间更短,时间更长——但我猜这是由于我的机器上的工作负载变化造成的)
同一查询的执行计划现在是:
有没有可能看到查询?以及两个表上的索引? 你的公羊有多大?每只公羊的一排有多大
case when b.AM IS null and b.ProgramName IS not null
then 'Unassigned'
else b.AM
end as AM