T-SQL code is extremely slow when saved as an Inline Table-valued Function

Go To StackoverFlow.com

6

I can't seem to figure out why SQL Server is taking a completely different execution plan when wrapping my code in an ITVF. When running the code inside of the ITVF on its own, the query runs in 5 seconds. If I save it as an ITVF, it will run for 20 minutes and not yield a result. I'd prefer to have this in an ITVF for code reuse. Any ideas why saving code as an ITVF would cause severe performance issues?

CREATE FUNCTION myfunction
(
    @start_date date, 
    @stop_date date
)
RETURNS TABLE 
AS
RETURN 
(
    with
    ad as (
        select [START_DATE]
              ,[STOP_DATE]
              ,ID
              ,NAME
              ,'domain1\' + lower(DOMAIN1_NAME)
               collate database_default as ad_name
        from EMP_INFO
        where DOMAIN1_NAME != ''
        union
        select [START_DATE]
              ,[STOP_DATE]
              ,ID
              ,NAME
              ,'domain2\' + lower(DOMAIN2_NAME)
               collate database_default as ad_name
        from EMP_INFO
        where DOMAIN2_NAME != ''
    )
    select ad.ID
          ,ad.NAME
          ,COUNT(*) as MONITORS
    from scores
    join users
        on (scores.evaluator_id = users.[user_id])
    join ad
        on (lower(users.auth_login) = ad.ad_name and
            scores.[start_date] between ad.[START_DATE] and ad.[STOP_DATE])
    where scores.[start_date] between @start_date and @stop_date
    group by ad.ID
            ,ad.NAME
)

EDIT:

Ok...I think I figured out the problem...but I don't understand it. Possibly I should post an entirely new question, let me know what you think. The issue here is when I call the function with literals, it is REALLY slow...when I call it with variables it is fast.

-- Executes in about 3 seconds
declare @start_date date = '2012-03-01';
declare @stop_date date = '2012-03-31';
select *
from myfunction(@start_date, @stop_date);

--Takes forever!  Never completes execution...
select *
from myfunction('2012-03-01', '2012-03-31')

Any ideas?

2012-04-04 19:17
by pyrospade
for starters (lower(users.auth_login) = ad.ad_name is non SARGabl - SQLMenace 2012-04-04 19:19
Nice! Wasn't familiar with SARG, good info. I have removed the lower() function and I'm still having the same issue. In fact, I've separated the logic into a view, removed out the aggregate portion, and I can do the aggregate work on the view. But once again, if I wrap that view in a function, I get the same problem.. - pyrospade 2012-04-04 19:57
Can you post the inline & non-inline execution plans - EBarr 2012-04-04 20:06
Will the estimated plans do? I'm thinking the ITVF will take hours. When I did the estimated plans last time I got a "missing index" on the ITVF and good results on the raw code. I will post them in a moment - pyrospade 2012-04-04 20:47
Strange, I just recreated the function to test it and it executes in 4 seconds! Maybe one of the DBAs altered something...very difficult to identify the issue now - pyrospade 2012-04-04 21:04
I've identified that using literals instead of variables slows the query way down and have updated the question to reflect that. Any ideas - pyrospade 2012-04-06 20:50


5

When you use literals SQL Server can look at the column statistics to estimate how many rows will be returned and choose an appropriate plan based on that assumption. When you use variables the values are not known at compile time so it falls back on guesses.

If the plan is better when it guesses than when it refers to the actual statistics then this indicates the statistics likely need updating.

If you have auto update of statistics turned on then you may well be hitting the issue here Statistics, row estimations and the ascending date column

2012-04-06 21:39
by Martin Smith
Wow, never would have guessed. Good info! Will up-vote as soon as I get the reputation - pyrospade 2012-04-06 22:05
Ads