存储过程非常慢,与具有相同结构的快速视图相比

Posted

技术标签:

【中文标题】存储过程非常慢,与具有相同结构的快速视图相比【英文标题】:stored procedure very slow, versus fast view with the same construction 【发布时间】:2013-11-06 15:48:28 【问题描述】:

我有一个针对特定参数运行非常缓慢的存储过程。

存储过程返回一个数据集。

我将存储过程转换为视图。我声明了相同的参数和值,它会在几秒钟内返回数据集。

存储过程需要五分钟。

我将问题缩小到存储过程中的特定子查询。删除子查询使存储过程能够立即返回数据。我将子查询转换为临时表并加入临时表而不是子查询,现在存储过程会在几秒钟内返回数据。

初始视图与存储过程测试中的操作相同。视图的执行方式有什么不同吗?

这是使用新临时表的存储过程。原来的子查询被注释掉了:

 (@JCCo bCompany,  
  @BegContract bContract ='', 
  @EndContract bContract ='zzzzzzzzzz', 
  @ThruMth bDate,
  @PM int = 0,
  @LabCTs varchar(10)=null,
  @MatCTs varchar(10)=null,
  @BegDept varchar(10) ='', 
  @EndDept varchar(10) ='zzzzzzzzzz')


With Recompile
as
--move the dates into a temp table
Select JCJM.JCCo, JCJM.Contract, JCCD.Job,
                        LastLaborDate=max(Case when JCTransType='PR' and (CostType between 2 and 3) then ActualDate else null end),
                        LastProjCostDate=max(Case when JCTransType='PF' then ActualDate else null end)
                    INTO #DATES
                   From JCCD JCCD with(NoLock)
                   Join JCJM JCJM with(nolock) on JCJM.JCCo=JCCD.JCCo and JCJM.Job=JCCD.Job
                    where (JCTransType='PR' or JCTransType='PF') and JCCD.JCCo=@JCCo and JCCD.Mth<=@ThruMth 
                            and JCJM.Contract Between @BegContract and @EndContract
                    group by JCJM.JCCo,JCJM.Contract,JCCD.Job
      -- ) as Dates on j.JCCo=Dates.JCCo and j.Contract=Dates.Contract and j.Job=Dates.Job




Select  JCCD.JCCo, JCCD.Job, JobDesc=j.Description, b.Contract, ContrDesc=b.Description, 

    --  MAX(Dates.LastLaborDate) as maxLastLaborDate, MAX(Dates.LastProjCostDate) as maxLastProjCostDate, 
    TD.LastLaborDate, TD.LastProjCostDate, 
--      '01/01/2013' AS LastLaborDate, '01/01/2015' AS LastProjCostDate,

        JCCD.Mth, JCCD.Phase, PhsDesc=JCJP.Description, 
        JCCD.CostType, CTDesc=JCCT.Description, JCCT.JBCostTypeCategory, 
        FieldPctCompl=isnull(u.PctCompl,0), FieldDate=Max(u.Date),  PMName=p.Name, 
        EstHours=sum(EstHours), EstCost=sum(EstCost), JTDHrs=sum(ActualHours), JTDCost=sum(ActualCost),
        ProjHours=sum(JCCD.ProjHours), ProjCost=sum(JCCD.ProjCost),
        MTDHrs=sum(case when JCCD.Mth=@ThruMth then ActualHours else 0 end), 
        MTDCost=sum(case when JCCD.Mth=@ThruMth then ActualCost else 0 end),
        FActCost=SUM(Case when JCCO.ProjMethod=2 then JCCD.ActualCost+JCCD.RemainCmtdCost else JCCD.ActualCost end),
        b.Department, DeptDesc=d.Description, HQName=HQCO.Name,ContrStat=b.ContractStatus,UIAmt=0,UITaxAmt=0,UIMiscAmt=0,ToBeInvd=0,sumTotalCmtdCost=0

from JCCD JCCD with(NoLock)
Left Outer Join JCJM j with(NoLock) on JCCD.JCCo=j.JCCo and JCCD.Job=j.Job
Left Outer Join JCCM b with(NoLock) on j.JCCo=b.JCCo and j.Contract=b.Contract
Left Outer Join JCDM d with(noLock) on b.JCCo=d.JCCo and b.Department=d.Department
Left Outer Join JCMP p with(NoLock) on j.JCCo=p.JCCo and j.ProjectMgr=p.ProjectMgr
Left Outer Join JCJP JCJP with(NoLock) on JCCD.JCCo=JCJP.JCCo and JCCD.Job=JCJP.Job and JCCD.Phase=JCJP.Phase
Left Outer Join JCCT JCCT with(NoLock) on JCCD.PhaseGroup=JCCT.PhaseGroup and JCCD.CostType=JCCT.CostType

Left Outer Join (Select u.Co, Job=u.Contract/*Prints Job# 9/13 per Tina - - VU9148*/, JCJM.Contract,
                        u.Phase, u.CostType, a.Mth, 
                        Date=Max(isnull(Date,'1950-01-01')),PctCompl=isnull(PctCompl,0)
                  from udVPPctComplete u
                  Join JCJM on u.Co=JCJM.JCCo and u.Contract=JCJM.Job
                  Join (select Co, Job=a.Contract,Contract=JCJM.Contract, Phase, CostType, Mth=Max(isnull(Mth,'1950-01-01')) 
                          from udVPPctComplete a 
                          Join JCJM on a.Co=JCJM.JCCo and a.Contract=JCJM.Job
                          where Mth<=@ThruMth and Co=@JCCo and JCJM.Contract between @BegContract and @EndContract
                          group by Co, a.Contract,JCJM.Contract, Phase, CostType
                     )a on a.Co=u.Co and a.Job=u.Contract and a.Phase=u.Phase and  a.CostType=u.CostType and a.Mth=u.Mth and a.Contract=JCJM.Contract
                group by u.Co, u.Contract,JCJM.Contract, u.Phase, u.CostType, a.Mth, isnull(PctCompl,0)
    ) u on JCCD.JCCo=u.Co and b.Contract=u.Contract and JCCD.Phase=u.Phase and JCCD.CostType=u.CostType and u.Job=JCCD.Job

    LEFT OUTER JOIN #DATES TD ON  j.JCCo=TD.JCCo and j.Contract=TD.Contract and j.Job=TD.Job
--Left Outer Join (Select JCJM.JCCo, JCJM.Contract, JCCD.Job,
--                      LastLaborDate=max(Case when JCTransType='PR' and (CostType between 2 and 3) then ActualDate else null end),
--                      LastProjCostDate=max(Case when JCTransType='PF' then ActualDate else null end)
--                 From JCCD JCCD with(NoLock)
--                 Join JCJM JCJM with(nolock) on JCJM.JCCo=JCCD.JCCo and JCJM.Job=JCCD.Job
--                  where (JCTransType='PR' or JCTransType='PF') and JCCD.JCCo=@JCCo and JCCD.Mth<=@ThruMth 
--                          and JCJM.Contract Between @BegContract and @EndContract
--                  group by JCJM.JCCo,JCJM.Contract,JCCD.Job
--     ) as Dates on j.JCCo=Dates.JCCo and j.Contract=Dates.Contract and j.Job=Dates.Job

Join JCCO JCCO with(NoLock) on JCCD.JCCo=JCCO.JCCo
Join HQCO HQCO with(NoLock) on JCCD.JCCo=HQCO.HQCo

where  JCCD.JCCo=@JCCo and b.Contract/*=' 51431.'--*/ between @BegContract and @EndContract and JCCD.Mth<=@ThruMth 
        and b.Department>=@BegDept and b.Department<=@EndDept 
        and j.ProjectMgr=(case when @PM<>0 then @PM else j.ProjectMgr end)

        and (charindex(','+left(cast(JCCD.CostType as char(5))+'     ', len(convert(varchar, JCCD.CostType)))+',', ','+@LabCTs+',')<>0 
                or charindex(','+left(cast(JCCD.CostType as char(5))+'     ', len(convert(varchar, JCCD.CostType)))+',', ','+@MatCTs+',')<>0 )
Group by JCCD.JCCo, JCCD.Job,j.Description, b.Contract, b.Description, JCCD.Mth, JCCD.Phase, JCJP.Description, JCCD.CostType,
            u.PctCompl,JCCT.Description, p.Name,JCCT.JBCostTypeCategory,
                    TD.LastLaborDate, TD.LastProjCostDate, 

            --Dates.LastLaborDate, Dates.LastProjCostDate,
            b.Department, 
            d.Description,HQCO.Name,b.ContractStatus

【问题讨论】:

请给我们两个代码。 显然视图和存储过程的行为方式不同,尽管它们可能产生相同的结果。子选择是……困难的:它们可以为每个结果行执行。并请提供SQL语句和程序代码。 我的猜测是参数嗅探。 SQL Server 根据使用的参数确定过程第一次运行时的执行计划。然后将该计划缓存并重用。对于视图,将为您运行的引用该视图的select 语句生成计划。两者可能完全不同。 已编辑以显示修改后的存储过程,包括注释掉的子查询 如果您仍在调查之前的性能不佳,您可以检查 subselect (max) 中使用的列是否被索引。 【参考方案1】:

如果两个查询字面意思相同,那么您的存储过程中可能缓存了损坏的查询计划。

您可以通过在过程中添加OPTION(RECOMPILE) 来强制过程每次重新编译计划。

Kim Tripp on OPTION(RECOMPILE)

如果我知道一个特定的语句在不同的执行之间有很大的不同,并且最佳计划也不同(同样,我应该通过测试多个示例执行知道这一点),那么我将正常创建存储过程并使用 OPTION (RECOMPILE) 以确保语句的计划没有被缓存或与存储过程一起保存。在每次执行时,该存储过程将获得不同的参数,特别讨厌的语句将在每次执行时获得一个新计划。

EDIT 现在我知道您正在调用重新编译。我将把这个答案留在这里,但对于其他搜索者来说,因为它可能与他们相关。

【讨论】:

是的,它们实际上是相同的,我希望执行是相同的。我认为存储过程必须对日期信息进行逐行评估。

以上是关于存储过程非常慢,与具有相同结构的快速视图相比的主要内容,如果未能解决你的问题,请参考以下文章

视图触发器事务存储过程函数流程控制索引与慢查询优化

MySQL-视图-触发器-事务-存储过程-函数-流程控制-索引与慢查询优化-06

数据库---视图,触发器,事务,存储过程 ,函数,流程控制, 索引与慢查询优化,测试索引,数据库三范式(简介)

插入数十万行时,MySQL 与 MS Access 相比非常慢

为啥声明表变量与临时表相比非常慢?

MySQL拓展 视图,触发器,事务,存储过程,内置函数,流程控制,索引,慢查询优化