2012-01-30 111 views
0

我有一张表格每晚都被插入,然后作为报表查询。SQL Server - 存储过程性能问题

为查询工作的存储过程具有动态SQL字符串,分页和其中的两个临时表。

它非常适用第一周

从第二周开始,它的性能开始急剧下降(花费3.5分钟时的恢复)

我已经获得并运行动态的输出字符串SQL,它显着更快(2秒),所以我猜它可能与编译器相关

然后我做了一些优化,如将count(*)更改为count(event_id),表现立即回来,但第二天早上表现再次下降。

然后我改变select into明确声明临时表,表现马上回来,但第二天早上表现再次下降。

然后我改变声明临时表明确回select into,表现立即回来,但第二天早上表现再次下降。

所以我想它无关代码优化,似乎每一次SP得到了编译时间,性能只能为不到24小时

我想到夜间插入,这是更好也24小时循环,然后我发现这个with (nolock)的事情,这可能已经锁定了表1

加入Nolock后,该存储过程运行良好了一个星期,在这之后我们再次得到了同样的问题,不同的是这一次,只有调用SP的网页很慢,从DB运行的SP很快...

这里是动态SQL存储过程:

CREATE PROCEDURE [dbo].[fs_1_usp_query] 
@paramerter_client_id int = null, 
@paramerter_event_type_id int = null, 
@paramerter_start_date   datetime = null, 
@paramerter_end_date   datetime = null, 
@paramerter_page_index int = 1, 
@paramerter_sort_direction varchar(20), 
@paramerter_page_count int = 30 
AS 
BEGIN  
SET ARITHABORT ON; 
SET NOCOUNT ON; 

declare @sql nvarchar(max) 

set @sql =  ' 

    create table #output2  
    ( 
      page_index          int,  
       rownumber           int, 
      page_count       int,  
      client_id       int,  
      date           datetime, 
    ) 




     --insert into #output1 
    select 
    page_count = count(event_id) over(),  
    table1.* 
    into #output1' 
    set @sql = @sql + ' 
    from 
    table1 table1 with (nolock) 
    inner join 
    table2 table2 with (nolock) 
    on 
    ............................ 
    inner join 
    table3 table3 with (nolock) 
    on 
    ............................ 
    inner join 
    table4 table4 
    on 
    ............................ 
    where 
    ............................ 

if (@paramerter_client_id is not null) 
    set @sql = @sql + ' and table2.client_id = @paramerter_client_id' 

if (@paramerter_event_type_id is not null) 
    set @sql = @sql + ' and table2.event_type_id = @paramerter_event_type_id' 

if (@paramerter_start_date is not null) 
    set @sql = @sql + ' and table2.created_date >= @paramerter_start_date' 
if (@paramerter_end_date is not null) 
     set @sql = @sql + ' and table2.created_date <= @paramerter_end_date' 

declare @lv_begin_index int 
declare @lv_end_index int 
set @lv_begin_index = ((@paramerter_page_index - 1) * @paramerter_page_count) + 1  
set @lv_end_index = @lv_begin_index + @paramerter_page_count  

set @sql = @sql + ' 

UPDATE #output1 
    SET osat_rating = ''-'' 
    WHERE LEFT(osat_rating , 1) = ''-''   

insert into #output2 
select  
    page_index = ' + convert(varchar, @paramerter_page_index) + ',  
    row_number() over (order by [' + @paramerter_sort_expression + '] '+ @paramerter_sort_direction + ') as rownumber, 
    #output1.* 
from #output1 

select #output2.* 
from #output2 
where 
    rownumber >= ' + convert(varchar, @lv_begin_index) + ' 
and 
    rownumber < ' + convert(varchar, @lv_end_index) ' 

set @sql = @sql + '  
drop table #output1  
drop table #output2 '* 

下面是静态SQL的快照试图按照你的建议:

Where 
    Column3 = Coalesce(@parameter3, Column3) 
    and 
    (@start_date is null or Column_created_date >= @start_date) 

    and 
    (@param_1 is null 
      or 
       (@param_1 not in (‘ConstantString1’, 'ConstantString2') and Column1 = @param_1) 
     or 
      (@param_1 = ‘ConstantString1’ and Column1 like 'ConstantString1%') 
     or 
      (@param_1 = ‘ConstantString2’ and (Column1 is null or Column1 = '')) 
) 
If(@parameter_sort_direction = 'DESC') 
Begin 
    insert into #temp_table_result 
    select  
     page_index = convert(varchar, @parameter_page_index),   
     row_number() over 
     (
      order by CASE 
        WHEN @parameter_sort_expression = 'Column1' THEN Column1 
        WHEN @parameter_sort_expression = 'Column2' THEN Column2 
        WHEN @parameter_sort_expression = 'Column3' THEN Column3 
        WHEN @parameter_sort_expression = 'Column4' THEN Column4 
        WHEN @parameter_sort_expression = 'Column5' THEN Column5 
        WHEN @parameter_sort_expression = 'Column6' THEN Column6 
        WHEN @parameter_sort_expression = 'Column7' THEN Column7 
        WHEN @parameter_sort_expression = 'Column8' THEN Column8 
       END desc--CASE 
       --  WHEN @parameter_sort_direction = 'ASC' THEN asc 
       --  WHEN @parameter_sort_expression = 'DESC' THEN  desc    
       --END 
     ) as rownumber, 
     #temp_table_staging.* 
    from #temp_table_staging 
END 
+1

你能为我们定义“快”和“慢”吗?我有需要几天运行的特效,还有一些会考虑0.5s很慢。 – JNK 2012-01-30 13:08:45

+2

并且在生产代码中远离Nolock - 它会导致您错过已经提交的行。 – 2012-01-30 13:37:09

+0

感谢您的问题。使用相同的数据集时,正常工作需要2秒钟,如果不正常,需要约3.5分钟。 – Ree5un 2012-01-30 14:54:02

回答

2

这是有可能的查询使用创建的统计数据随着时间的推移,计划逐渐过时。

考虑在受查询影响的表上每6小时更新一次统计信息 - 如果可能,请在开发环境中对其进行测试。

0

我建议您在每次sp开始时尝试使用选项WITH RECOMPILE来刷新执行计划。

而且存在对于这样的情况下优化执行计划的一些技术设备: http://msdn.microsoft.com/en-us/library/ms181714.aspx

例如:

OPTIMIZE FOR 

PARAMETERIZATION 

希望这将有助于。通过

and (@paramerter_client_id IS NULL OR table2.client_id = @paramerter_client_id) 

当然

 if (@paramerter_client_id is not null) 
     set @sql = @sql + ' and table2.client_id = @paramerter_client_id' 

,不要忘记创建对表2的索引:

+0

我刚刚添加了一些代码示例,以查明格式化是否搞乱了,所以请忽略此评论。 – Ree5un 2012-02-14 15:06:31

0

我建议你完全避免动态SQL,您可以替换像这样的一些代码.client_id!

+0

感谢您的提示:=) 是的,关于计划缓存,我正在启动动态SQL并执行静态SQL,希望在接下来的两周内我会回到这里更新它是否会生效: – Ree5un 2012-02-14 14:57:07

+0

刚刚添加如果你有时间,草案中的草案发生了变化,请帮助我看看,如果有任何问题,谢谢! – Ree5un 2012-02-14 15:15:29

+0

你好, 你的SQL听起来更好:)如果你的存储过程没有返回太多的行,你可以使用内存中的临时表(http://odetocode.com/code/365.aspx)以避免使用的tempdb数据库。 要增加应用程序的spped,可以避免在SQL Server中对数据进行排序,并在应用程序端执行该操作。如果您使用.NET平台,则可以例如实现IComparer接口。 – schglurps 2012-02-14 20:35:39