SQL User Defined Functions Are Slower Than Inline Logic

Posted September 21, 2007 at 1:47 PM

Tags: SQL

In one of my other posts on comparative SQL performance and optimization, Jason Rushton stated that in his experience, using a SQL user defined function in a query performed up to 4 times slower than the equivalent inline logic. Gus also backed this up with his opinion that UDFs should be used only when necessary.

If you stop and think about it, user defined functions in any language are going to be slower than using the equivalent inline logic; it adds more processing instructions and overhead to the overall algorithm and more processing = more slower. To see this in action, I set up this little test:

 Launch code in new window » Download code as text file »

  • <!---
  • Get all the IDs as strings using a SQL user
  • defined function, ToString().
  • --->
  • <cftimer
  • type="outline"
  • label="User Defined Function">
  •  
  • <!--- Start running total. --->
  • <cfset intTotal = 0 />
  •  
  • <!--- Loop over this method 10 times. --->
  • <cfloop index="i" from="1" to="10" step="1">
  •  
  • <!--- Query for IDs. --->
  • <cfquery name="qID" datasource="#REQUEST.DSN.Source#" result="objResult">
  • SELECT
  • dbo.ToString( b.id ) AS id_string
  • FROM
  • blog_entry b
  • </cfquery>
  •  
  • <!--- Output execution time of query. --->
  • #objResult.ExecutionTime# ms /
  • #qID.RecordCount# records
  • <br />
  •  
  • <!--- Update total. --->
  • <cfset intTotal = (intTotal + objResult.ExecutionTime) />
  •  
  • </cfloop>
  •  
  • <!--- Output average run time. --->
  • Average: #Fix( intTotal / 10 )#<br />
  •  
  • </cftimer>
  •  
  •  
  • <!--- Get all the IDs as strings using inline SQL. --->
  • <cftimer
  • type="outline"
  • label="Inline Logic">
  •  
  • <!--- Start running total. --->
  • <cfset intTotal = 0 />
  •  
  • <!--- Loop over this method 10 times. --->
  • <cfloop index="i" from="1" to="10" step="1">
  •  
  • <!--- Query for IDs. --->
  • <cfquery name="qID" datasource="#REQUEST.DSN.Source#" result="objResult">
  • SELECT
  • LTRIM( RTRIM( STR( b.id ) ) ) AS id_string
  • FROM
  • blog_entry b
  • </cfquery>
  •  
  • <!--- Output execution time of query. --->
  • #objResult.ExecutionTime# ms /
  • #qID.RecordCount# records
  • <br />
  •  
  • <!--- Update total. --->
  • <cfset intTotal = (intTotal + objResult.ExecutionTime) />
  •  
  • </cfloop>
  •  
  • <!--- Output average run time. --->
  • Average: #Fix( intTotal / 10 )#<br />
  •  
  • </cftimer>

Here, we are converting the row ID to a string, first by using the SQL user defined function, ToString() and then afterwards by putting the same logic inline. Running the above code, we get the following times:

SQL User Defined Function

178 ms
157 ms
165 ms
179 ms
139 ms
160 ms
160 ms
68 ms
154 ms
140 ms

Average time: 150 ms

Equivalent Inline SQL Logic

100 ms
142 ms
157 ms
100 ms
115 ms
156 ms
135 ms
156 ms
159 ms
100 ms

Average time: 132 ms

Note: I ran the code 10 times (listed above) giving me the average of 10 averages.

When running this on a 50,000+ record set, it looks like (as would be expected) the inline logic is performing better than the user defined function. I am not, however, seeing the 4x speed difference that Jason had mentioned. Of course, my logic is uber simple and the UDF logic is also very small.

While the difference here does not jump out and slap you on the ass, the difference in performance is there; as such, it is something to think about when you are optimizing your queries. Of course, you have to take into account the ease of coding vs. performance requirements. If putting logic inline gives you a 30 ms improvement on a report that gets run once a day, is that worth NOT getting the benefits of a UDF? Probably not. But if this is a query that either sees a huge performance hit, slows down the rest of a site, or gets run very often, every few milliseconds you can squeeze out can make a difference.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page



Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Sep 21, 2007 at 2:22 PM // reply »
19 Comments

CFC's are slower than inline logic as well... ;)

<ducks>


Sep 21, 2007 at 2:34 PM // reply »
7,572 Comments

Ha ha ha :)


Sep 21, 2007 at 4:02 PM // reply »
35 Comments

Assembly is faster than CF

**Grabs Pie and throws**

DW


Ian
Sep 21, 2007 at 4:16 PM // reply »
7 Comments

My Mustang is faster than your Camero.


Sep 22, 2007 at 4:49 AM // reply »
6 Comments

User Defined Functions can also execute faster on average then inline logic if they are immutable and the database supports result caching.

However, the real problem of User Defined Functions is not the overhead in invoking them, but that from the perspective of the planner they usually behave like a black box. They do not have an adequate selectivity definition and they do not have an adequate cost definition. For a thorough explanation you should read Optimization of Queries with User-Defined Predicates http://ee.snu.ac.kr/~shim/tods.pdf


Sep 23, 2007 at 11:58 AM // reply »
7,572 Comments

@Jochem,

I have put that PDF in my "To Be Reviewed" folder. Skimming over it, it seems to have a lot of higher level math and things of that nature... gonna need to set aside some time to really pick through it. Thanks.


Sep 24, 2007 at 2:37 AM // reply »
129 Comments

I think it's important to note that a procedure invocation in a language like SQL need not be slower than the equivalent inline logic. Specifically when the logic is complex.

SQL is parsed (generally speaking), at runtime, and reparsed every time the query is executed unless the database is really smart about caching operations. Parameterizing the query usually helps significantly, and depending on the database, may provide *massive* performance boosts. For instance Oracle bogs down really fast unless you use bind params.

However, complex logic that can be wrapped up in a UDF can provide a real performance boost if the database can determine that the UDF can be compiled and stored and then later when a dynamic query is executed, that the database needs to recompile each time, that part of the query will be excluded from the recompile.

Nothing prevents a database from inlining the logic right into the real query if the database can determine the resulting (or lack of) side effects.

We should look at a lot of factors when deciding how to structure our database calls and how to handle queries, because there's a whole lot more going on than a simple function call that's going to get in the way of performance.

I'd certainly be more worried about indexes, materialized or indexed views, and most importantly the algorithms, such as set based solutions to complex tasks, instead of worrying about the constant time execution overhead that comes from UDF invocation.


Sep 24, 2007 at 7:22 AM // reply »
7,572 Comments

@Elliott,

All good points. I think I am still at a point where I don't have complex logic either inline or in UDFs. Perhaps I am just not experienced enough to see how much of my processing can be moved into SQL (as opposed to trying to leverage ColdFusion post-query).


Oct 17, 2007 at 8:18 PM // reply »
1 Comments

Elliot - when you say "Nothing prevents a database from inlining the logic right into the real query if the database can determine the resulting (or lack of) side effects", what factors go into allowing the database to determine a lack of "side effects" and what possible side effects could there be of a UDF?

I ask because we have a stored procedure that takes a @firstName parameter and does this

SELECT x, y, z
WHERE udf_CompareText(x, @name) = 1

to offload a multistep comparison process. Scaled down a bit, the udf looks like this:

CREATE FUNCTION udf_CompareText (@param varchar(50), @valueInTable varchar(50))

...

IF (
(@param = @valueInTable) OR
(charindex(@param, @valueInTable) > 0) OR
(charindex(@valueInTable, @param) > 0) OR
(soundex(@param) = soundex(@valueInTable))
) return 1

etc.

We have an index on the firstName column. Inlining this comparison is significantly faster than doing it in the UDF. Is there any way to help my database engine (SQL Server) to, as you say, inline the logic implicitly?


Feb 9, 2009 at 10:49 AM // reply »
1 Comments

I can confirm that UDFs (table functions - not scalar) are up to 4x slower than inline SQL logic. I just created an UDF with a simple select statement. It takes 60s+ to complete while the plain select statement is finished after 15s. I have no idea how this MAY happen.


Mar 9, 2009 at 4:34 AM // reply »
1 Comments

inline is prone to SQL Injection attack. Its more faster that other but more vulnerable to SQL injection attacks.


Post Comment  |  Ask Ben

Recent Blog Comments
Mar 22, 2010 at 7:43 AM
Terms Of Service / Privacy Policy Document Generator
Thankyou for this very helpful form. You've made my life much easier today. I'll have a look around your site... I'm sure there's some more good stuff here..Thanks Dave ... read »
Mar 22, 2010 at 7:21 AM
Encountered "(. Incorrect Select Statement, Expecting a 'FROM', But Encountered '(' Instead, A Select Statement Should Have a 'FROM' Construct.
I got this exception now. In case you're using var-es local struct, CF gives you couple of "new" exceptions: Encountered "local. and Encountered "id. Incorrect Select List, Incorrect select colum ... read »
Mar 22, 2010 at 3:08 AM
Ask Ben: Selecting XML Attributes Given Other XML Attributes
Thanks for the response. I finally discovered that I was getting this error because I had cfsetting enablecfoutputonly="yes" in Application.cfc, and was neither setting it to false elsewhere nor brac ... read »
Mar 21, 2010 at 8:57 PM
The Bourne Ultimatum Starring Matt Damon And Julia Stiles
late to the party, but my observation is this: rewatch carefully for the platonic nature of the relationship between nicki and jason. she never flirts with him. he never comes on to her. they alway ... read »
Mar 21, 2010 at 7:40 PM
Is Simulating User-Input Events With jQuery Ever A Good Idea?
A couple of things. One you embed the initial state of of more-info in the CSS. IMHO, that behavior should be in jQuery: moreInfo.hide(); It shows that the behavior your toggling and closing is mor ... read »
Mar 21, 2010 at 3:59 PM
Exploring ColdFusion Component Runtime Class Properties And Serialization
@Elliott, according to Ben's experiment, serializeJSON() doesn't access the private data by default - it doesn't even access the getHair() method - so trying to clone a Girl.cfc via serializeJSON/des ... read »
Mar 21, 2010 at 3:49 PM
Ask Ben: Javascript String Replace Method
I'm confused a bit by what you are asking, but if had this sentence: The color, red, is in the style statement; style: red;. and wanted to remove all or change all of the commas, colons, and semi-c ... read »
Mar 21, 2010 at 3:13 PM
Ask Ben: Javascript String Replace Method
I am trying to make a java program to count the number of times that these punctuation marks occur in a body of text: , : ; . ! - ' " ? / \ I am using this piece to ferret out the commas: numcommas ... read »