存储过程非常慢,与具有相同结构的快速视图相比
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
数据库---视图,触发器,事务,存储过程 ,函数,流程控制, 索引与慢查询优化,测试索引,数据库三范式(简介)