Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 Transact-SQL (2000)
 Parameter vs Hard Coded performance problem

Author  Topic 

morleyhill
Starting Member

19 Posts

Posted - 2004-10-18 : 06:50:58
Hi all

Can anyone explain why I'm getting a serious performance problem when I pass a variable to a user defined function.
Here is the scenario-

if I execute the following:
declare @ShortName nvarchar(40)
set @ShortName = 'my agency'
select * from dbo.MyFunction(@ShortName)
it takes 60-seconds to run!

but if I execute this:
select * from dbo.MyFunction('my agency')
it takes 1-second to run.

All other things being the same, surely these two statements should execute the same, or am I missing something? Any suggestions please!



nr
SQLTeam MVY

12543 Posts

Posted - 2004-10-18 : 06:56:36
Have a look at the query plans

==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

morleyhill
Starting Member

19 Posts

Posted - 2004-10-18 : 07:06:42
OK- when I pass the parameter it uses a 'Table Scan' which takes up 99% of the 60-second run time, whereas when I pass the hard coded value it performs an 'Index Seek' which takes no time at all.

So, now I know this difference, what can I do about it?
Go to Top of Page

jsmith8858
Dr. Cross Join

7423 Posts

Posted - 2004-10-18 : 09:11:00
what is the datatype declared of the parameters the UDF expect?

how does

select * from dbo.MyFunction(N'my agency')

perform?

- Jeff
Go to Top of Page

morleyhill
Starting Member

19 Posts

Posted - 2004-10-18 : 09:16:15
Q. what is the datatype declared of the parameters the UDF expect?
A. The parameter is declared as nvarchar(40)

Q. how does
select * from dbo.MyFunction(N'my agency')
perform?
A. The same as
select * from dbo.MyFunction('my agency'), i.e. it runs in about 1-second
Go to Top of Page

morleyhill
Starting Member

19 Posts

Posted - 2004-10-18 : 10:49:13
For anyone that's interested, here's the fix-

The UDF is an inline table value function that uses a SQL select to return statistical data. One of the tables in the SQL select has over 2,000,000 records. Tests have shown that the SQL join on this table uses Index Seek when I pass the hard coded value but a Table Scan when I pass the parameter. However, if I delete some records (leaving around 1,400,000 records) then the select uses the preferable Index Seek in both scenarios.
I've got around the query using a Table Scan by specifying an 'inner loop join' hint, to force the function to use the index.

This does raise a more general issue of functions executing quickly, by using an available index, upto a point. Then slow down dramatically when an apparently arbitrary record number threshold has been met. This may be something peculiar to my database, or has anyone come across this before?

I can see me having a long week while I check the execution plan of all 1000+ UDFs and SPs!

Thanks to everyone who took the time to ponder my dilemma!

Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2004-10-18 : 11:04:06
Why are you using unicode?

And I don't believe for a second that deleting records is the "fix".

And maybe you could actually post the Code for the function, and the sample of what you actually passed.

And why use a function at all?

And what's the cardinality of that column?



Brett

8-)
Go to Top of Page

morleyhill
Starting Member

19 Posts

Posted - 2004-10-18 : 12:10:44
Brett-

Q. Why are you using unicode?
A. I can't answer that as there aren't any of the original dba's left on the project. I've been tasked with a number of enhancements and didn't design the original db. It might possibly have something to do with this databse being live in over 20-countries and not all of them speak English.

Q. And I don't believe for a second that deleting records is the "fix".
A. I've not said that deleting records was the 'fix', that was just an observation that I made while testing. The fix was to add the 'inner loop join' hint.
I mentioned deleting records because of the concerns that I went on to express.

Q. And maybe you could actually post the Code for the function, and the sample of what you actually passed.
A. The 'production' function is quite involved, so for testing I used the following which produced the similar performance issue. (dbo.tblPaymentAccount is the table containing over 2,000,000 rows). Note also that this has the hint fix.

create function dbo.fnPaymentAccount_Get_Payments_Agency_Period
(
@AgencyShortName nvarchar(40)
)
returns table
as
return
(
select e.RespondedTo,
pa.PaidIn,
pa.FirstPayment
from dbo.Enquirers e
join dbo.clients c on c.EnqId = e.EnqId
inner loop join dbo.tblPaymentAccount pa on pa.ClientId = c.ClientId
where e.Source = @AgencyShortName
)
The calling code is as posted above.

Q. And why use a function at all?
A. For the scenario that I have, it is the best solution. I'm not going to justify that because that isn't the issue. I've now observed this performance issue in UDF's, SP's and executing SQL straight from Query Analyzer.

Q. And what's the cardinality of that column?
A. If you mean unique values in Enquirers.Source then 48,500.

Go to Top of Page

Lumbago
Norsk Yak Master

3271 Posts

Posted - 2004-10-19 : 04:49:07
I'm no dba so I really don't know too much about this but could it be that the delete forces a statistics update? I know that for query plans to be worked out properly statistics must be recomputed every now and the and index defrag/reindex could also do you some good. If this is not taken care of allready, look up UPDATE STATISTICS, DBCC INDEXDEFRAG, DBCC DBREINDEX and DBCC UPDATEUSAGE in BOL

--
Lumbago
"Real programmers don't document, if it was hard to write it should be hard to understand"
Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2004-10-19 : 14:39:49
Hey, cool : What's an "inner loop join"??

I ain't got one of those in my code, and I so **WANT** one for my collection ...

Kristen
Go to Top of Page

morleyhill
Starting Member

19 Posts

Posted - 2004-10-20 : 06:18:17
Hi Lumbago-
Thanks for the tip, I'll check that out.

However, the problem was repeatable- so by adding records back into the table the query used an 'Index Seek' upto a certain row count threshold, over which it then used a 'Table Scan'.
When I then removed the records again below the row count threshold the query would revert to using the 'Index Seek'.
The row count threshold was about 1,400,000.
Go to Top of Page

morleyhill
Starting Member

19 Posts

Posted - 2004-10-20 : 06:29:07
Kristen-

An 'inner loop join' forces the query to use a loop join rather that a hash or merge join.

I've since found this article by Michael Balloni that describes this issue very well-
http://www.sql-server-performance.com/mb_sql_server_joins.asp

I feel a lot happier that this isn't something peculiar to my db, but it still doesn't explain why the original UDF call behaved differently when passing a variable to when passing a hard-coded value.

Go to Top of Page
   

- Advertisement -