我有一个存储过程,如果使用硬编码值,如''='',在4秒内执行,如下所示:
alter procedure proc1
( @filter varchar(400)=null )
as
begin
select a,b,c from tbl1 where ''=''
end
但是当我将它作为存储过程参数传递时,如下所示:
alter procedure proc1
( @filter varchar(400)=null )
as
begin
select a,b,c from tbl1 where @filter=''
end`
当我从我的应用程序调用它或者使用SQL Server中的execute语句直接执行它时,大约需要7到8分钟
exec proc1 ''
上面给出的sp只是一个例子。该过程实际上很大,包括许多select语句和用户定义的函数,但问题出现在上面给出的类似语句中。
条件:
where
et.X is not null and et.Y is not null and e.EquipmentName is not null AND e.Available='Available'
--and (Select dbo.GetFilterStatusOfEquipment(getdate(),'70','50','ON'))='*'
AND (
(dbo.GetFilterStatusOfEquipment(et.SignalDateTime,et.SpeedOfTheVehicle,(SELECT COUNT(J.JobId) FROM tbl_Notification J
inner JOIN tbl_NotificationAssign JN ON JN.NotificationNo =J.NotificationNo
inner JOIN dbo.tbl_CustomStatus JS ON JS.CustomStatusID=J.CustomStatusID
INNER JOIN dbo.tbl_SystemStatus ss ON ss.SystemStatusID=JS.SystemStatusID
WHERE JN.DriverID=et.DriverID AND ss.SystemStatusID !=9),et.IgnitionStatus)
in (Select val from Split(@filter,',')) or (@filter='')))
and (et.CompanyID=@CompanyID or @CompanyID='') AND e.Flag_Delete='0'
GetFilterStatusOfEquipment和Split是UDF。
“或(@ filter ='')”会减慢查询速度,但如果我将''=''直接写入查询并执行,它运行正常。
1.使用未直接显示在参数上的虚拟变量也可确保执行计划的稳定性,而无需添加重新编译提示,如下所示:
alter procedure proc1
( @filter varchar(400) = NULL)
as
begin
declare @filterDummy varchar(400)
set @filterDummy = @filter
select a,b,c from tbl1 where @filterDummy = ''
end
2.要防止此情况和其他类似情况,您可以使用以下查询选项:
OPTIMIZE FOR
RECOMPILE
3.批处理期间的禁用自动更新统计信息
也许您的查询很复杂,SQL Server需要很长时间才能生成计划。
在这种情况下,如果您的统计信息自动更新,我建议您使用动态查询和sp_executesql与查询选项KEEPFIXEDPLAN。
Declare @Query nvarchar(max),@QueryParameters nvarchar(max)
Set @Query='select a,b,c from tbl1 where @filter='''' OPTION(KEEPFIXED PLAN)'
Set @QueryParameter='@filter varchar(400)'
sp_executesql @Query,@QueryParameters,@filter=@filter
只有在启用自动更新统计信息时才会使用选项KEEPFIXED PLAN。
这是一篇关于使用KEEPFIXED PLAN选项重新编译存储过程的文章:https://support.microsoft.com/en-us/kb/276220
当您使用以下表达式时:
in (Select val from Split(@filter,',')) or (@filter=''
)
要么
et.CompanyID=@CompanyID or @CompanyID=''
如果[CompanyID]列具有索引或[CompanyID]是主键,则执行计划包含索引'scan operator'而不是'index seek'运算符。
在这种情况下,您应该生成查询。
例如:
alter procedure proc1
( @filter varchar(400)=null )
as
begin
declare @query nvarchar(1000)
set @query='select * from tbl'
if (@filter<>'')
set @query=@query+' where id in (Select val from split(@filter,'',''))'
EXECUTE sp_executesql @query ,
N'@filter nvarchar(30)',
@filter =@filter
end
或使用IF声明
alter procedure proc1
( @filter varchar(400)=null )
as
begin
if (@filter<>'')
select * from tbl
where id in (Select val from split(@filter,','))
else
select * from tbl
end