Home > OS >  How to resolve execution timeout expired issue occuring in a stored procedure
How to resolve execution timeout expired issue occuring in a stored procedure

Time:09-16

Facing timeout expired issue in a code developed. Shared below is the stored procedure where timeout occurs. Purpose of the code : Dates being passed from frontend (using a forloop in Windows application vb.net code) for 1 million cases for which date difference needs to be calculated basis the date received.

create procedure sp_getdatediff

@strd1 date = null,
@strd2 date = null,
@strd3 date = null,
@strd4 date = null,
@strd5 date = null,
@strd6 date = null,
@strd7 date = null,
@strd8 date = null,
@strd9 date = null,
@strd10 date = null,
@strd11 date = null
as
begin

declare @vardatediff1 int
declare @vardatediff2 int
declare @vardatediff3 int
declare @vardatediff4 int
declare @vardatediff5 int

set @vardatediff1 = [fn_getdiff](@strd1,@strd2,@strd3) ----- input parameters are dates passed from frontend
set @vardatediff2 = [fn_getdiff](@strd2,@strd4,@strd5)
set @vardatediff3 = [fn_getdiff](@strd4,@strd5,@strd6)
set @vardatediff4 = [fn_getdiff](@strd5,@strd7,@strd8)
set @vardatediff5 = [fn_getdiff](@strd9,@strd10,@strd11)

update tbl_Scheduler set col_dif1 = @vardatediff1 , col_dif2 = @vardatediff2 , 
col_dif3 = @vardatediff3 , col_dif4 = @vardatediff4 , col_dif5 = @vardatediff5
where id = @id

end

Function code :

create function [fn_getdiff]
(
@startdate date = null,
@enddate date = null,
@ccode varchar(10) = null
)
returns integer


as
begin
declare @count integer 
declare @tdaycount integer 
if (@startdate is null or @startdate = '')
begin
    set @count = 0
end
else if (@enddate is null or @enddate = '')
begin
    set @count = 0
end
else
begin

    select @tdaycount = count(distinct(convert(date,tdays))) from tbl_holidays with (nolock) where (convert(date,tdays,105) >= convert(date,@startdate,105))
    and (convert(date,tdays,105) <= convert(date,@enddate,105)) and tcode in (select id from tbl_code with (nolock) where id = @ccode)

    select @count  = datediff(@startdate,@enddate)
    
    set @count = @count - @tdaycount 
    
end
return @count 

end

Is there optimization required in this code to eliminate timeout issue? How can same be done?

CodePudding user response:

The use of IN in your function can be replaced by a JOIN.

Your query :

SELECT @tdaycount = COUNT(DISTINCT(CONVERT(DATE, tdays)))
FROM   tbl_holidays WITH(NOLOCK)
WHERE  (CONVERT(DATE, tdays, 105) >= CONVERT(DATE, @startdate, 105))
        AND (CONVERT(DATE, tdays, 105) <= CONVERT(DATE, @enddate, 105))
        AND tcode IN (SELECT id
                      FROM tbl_code WITH(NOLOCK)
                      WHERE id = @ccode);

The rewriting :

SELECT @tdaycount = COUNT(DISTINCT(CONVERT(DATE, tdays)))
FROM   tbl_holidays  AS h
       JOIN tbl_code  AS c
          ON h.tcode = c.id
WHERE    (CONVERT(DATE, tdays, 105) >= CONVERT(DATE, @startdate, 105))
          AND (CONVERT(DATE, tdays, 105) <= CONVERT(DATE, @enddate, 105))
          AND  c.id = @ccode;

When A = B and B = C the A = C, so :

...
              ON h.tcode = c.id
    WHERE    ...
              AND  c.id = @ccode;

The rewriting (2) :

SELECT @tdaycount = COUNT(DISTINCT(CONVERT(DATE, tdays)))
FROM   tbl_holidays  AS h
WHERE    (CONVERT(DATE, tdays, 105) >= CONVERT(DATE, @startdate, 105))
          AND (CONVERT(DATE, tdays, 105) <= CONVERT(DATE, @enddate, 105))
          AND  tbl.tcode = @ccode;

You do not respects rules of posting... So we cannot helps you so much. Put the description of your tables and indexes as DDL SQL Statement please. Probably the CONVERTs are useless...

CodePudding user response:

Your function contains this bit of SQL.

select @tdaycount = count(distinct(convert(date,tdays))) 
 from tbl_holidays with (nolock) 
where (convert(date,tdays,105) >= convert(date,@startdate,105))
and (convert(date,tdays,105) <= convert(date,@enddate,105)) 
and tcode in (select id from tbl_code with (nolock) where id = @ccode)

Date-conversion error

CONVERT(DATE, @enddate, 105) produces a date of the form 31-12-2006. I suspect it makes no sense to use <= comparisons on that format because 30-12-2007 will come before 31-12-2006. Your data is already in the DATE format, so you can compare it without converting it.

Sargability performance antipattern

It contains a couple of performance antipatterns like this:

WHERE function(column) >= function(constant)

This is an antipattern because it's a non-sargable filter. What you want is this sort of pattern

WHERE column >= constant

or

WHERE column >= function(constant)

combined with an apppropriate index on the table.

Index

A multicolumn index on your tbl_holidays table on (tcode, tdays) will allow the server to satisfy your queries by range-scanning that index. Try this.

CREATE INDEX tcode_tdays ON tbl_holidays (tcode, tdays); 

Rewrite

SELECT @tdaycount = COUNT(DISTINCT tdays) 
  FROM tbl_holidays WITH (NOLOCK)
 WHERE tdays >= @startdate
   AND tdays <= @enddate
   AND tcode in (SELECT id 
                   FROM tbl_code WITH (NOLOCK) 
                  WHERE id = @ccode)

I don't know how much time this will save. But it will save some.

Default timeout too short

.NET's SQL classes are set up with 30-second timeouts. If you're processing millions (lakhs upon lakhs) of rows that's not long enough. Try setting the timeout to ten minutes or whatever duration makes sense.

Dim command = conn.CreateCommand()
...
command.CommandTimeout = 600  ' ten minutes 
  • Related