THE SQL Server Blog Spot on the Web

Welcome to SQLblog.com - The SQL Server blog spot on the web Sign in | Join | Help
in Search

Adam Machanic

Adam Machanic, Boston-based independent database consultant, writer, and speaker, shares his experiences with programming, performance tuning, and optimizing SQL Server 2000, 2005, and 2008, in conjunction with related technologies such as .NET.

Performance: ISNULL vs. COALESCE

Mladen aka spirit1 posted a speed test of COALESCE vs. ISNULL. Reported result: COALESCE is faster.

But leave it to Anatoly Lubarsky to argue with what was posted. He posted his own speed test, showing that ISNULL is faster.

Anatoly's results showed a miniscule difference, "52 seconds" vs. "52-53 seconds". Mlanden's tests show a larger difference, around 15%. But I don't trust either of these results.

One thing in common with both of the tests I linked to, and which makes them both flawed, is that they return data to the client. This factors greatly into testing time. What if there was a network hiccup, or what if the client UI did something different when rendering the results? We're not testing the network's ability to send data or the client's ability to render it. What's being tested is very specific: Speed of COALESCE vs. ISNULL.

So this leads me to present Adam's Number 1 Rule of Performance Testing: When performance testing a specific feature, do everything in your power to test only that feature itself. Isolate your test as much as possible so that there is no way network traffic or unrelated UI code will get in the way. If you aren't careful about this, you will end up testing these other resources instead of your goal. And when testing against tables in SQL Server, it's especially important to be careful given SQL Server's caching mechanisms. So when testing using tables, I'll always throw out the first few test runs, or even restart the server between tests, in order to control the cache in whever way is logical for the feature being tested.

Before getting to my own tests, I'd like to jump off on a quick tanget. COALESCE vs. ISNULL? Who cares! This isn't a performance question, this is a question of standards-conformant vs. proprietary code. ISNULL is non-standard and provides less functionality than COALESCE. Yet a lot of SQL Server developers love to use it, I suspect because it's a lot easier to remember (and spell). So learn a new word and type two extra characters and you'll end up with more maintainable, more functional code. Sounds good to me -- which is why I am a big fan of COALESCE.

But I am still curious... Which is faster?

In this case, no test data is needed. We're testing performance of the COALESCE and ISNULL functions themselves, not using them to access data from a table. So the most effective test, in my opinion, is to run COALESCE and ISNULL a bunch of times each (one million) and see which runs faster:

 

DECLARE @i INT SET @i = 1
DECLARE @CPU INT SET @CPU = @@CPU_BUSY
DECLARE @StartDate DATETIME SET @StartDate = GETDATE()

WHILE @i <= 1000000
BEGIN
IF COALESCE('abc', 'def') = 'def'
PRINT 1
SET @i = @i + 1
END

PRINT 'COALESCE, both non-null'
PRINT 'Total CPU time: ' + CONVERT(varchar, @@CPU_BUSY - @CPU)
PRINT 'Total milliseconds: ' + CONVERT(varchar, DATEDIFF(ms, @StartDate, GETDATE()))
PRINT ''
GO

DECLARE @i INT SET @i = 1
DECLARE @CPU INT SET @CPU = @@CPU_BUSY
DECLARE @StartDate DATETIME SET @StartDate = GETDATE()

WHILE @i <= 1000000
BEGIN
IF ISNULL('abc', 'def') = 'def'
PRINT 1
SET @i = @i + 1
END

PRINT 'ISNULL, both non-null'
PRINT 'Total CPU time: ' + CONVERT(varchar, @@CPU_BUSY - @CPU)
PRINT 'Total milliseconds: ' + CONVERT(varchar, DATEDIFF(ms, @StartDate, GETDATE()))
PRINT ''
GO

DECLARE @i INT SET @i = 1
DECLARE @CPU INT SET @CPU = @@CPU_BUSY
DECLARE @StartDate DATETIME SET @StartDate = GETDATE()

WHILE @i <= 1000000
BEGIN
IF COALESCE(null, 'abc') = 'def'
PRINT 1
SET @i = @i + 1
END

PRINT 'COALESCE, first column null'
PRINT 'Total CPU time: ' + CONVERT(varchar, @@CPU_BUSY - @CPU)
PRINT 'Total milliseconds: ' + CONVERT(varchar, DATEDIFF(ms, @StartDate, GETDATE()))
PRINT ''
GO

DECLARE @i INT SET @i = 1
DECLARE @CPU INT SET @CPU = @@CPU_BUSY
DECLARE @StartDate DATETIME SET @StartDate = GETDATE()

WHILE @i <= 1000000
BEGIN
IF COALESCE(null, 'abc') = 'def'
PRINT 1
SET @i = @i + 1
END

PRINT 'ISNULL, first column null'
PRINT 'Total CPU time: ' + CONVERT(varchar, @@CPU_BUSY - @CPU)
PRINT 'Total milliseconds: ' + CONVERT(varchar, DATEDIFF(ms, @StartDate, GETDATE()))
PRINT ''
GO

You'll notice that I'm not using STATISTICS TIME to get the CPU and run time. Unfortunately, STATSTICS TIME returns once per statement, so it is not usable for this test -- we would wind up with one million 0 millisecond results. If you're running on a quiet server (and you should always run targeted performance tests on a quiet server; that may have to become Adam's Number 2 Rule if I can't think of something better) @@CPU_BUSY will give a close enough approximation of how much CPU time the test is using. And DATEDIFF will give us a good enough time reading. Note that the predicate in the IF statement will never return true, so we know that we're not testing our network or client.

I ran these tests several times on a few different servers, and ISNULL appears to pretty consistently out-perform COALESCE by an average of 10 or 12 percent. But that's the difference between 6 seconds and 5.3 seconds (the approximate average runtimes per test on my servers), over the course of a million exections. Hardly worth the functionality and standards compliance sacrifice, at least in the scenarios I use these functions for.



Published Wednesday, July 12, 2006 10:16 PM by Adam Machanic
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Thomas said:

There's a possible pitfall with coalesce. Try this:

declare @t table (id int, f1 int, f2 int)

insert into @t values(1,1,null)

insert into @t values(2,1,null)

insert into @t values(3,1,2)

insert into @t values(4,1,null)

insert into @t values(5,1,5)

insert into @t values(6,1,null)

declare @p int

set @p = null

select f2, coalesce(@p,f2) as [coalesce(@p,f2)], [f2=coalesce] = case

when f2 = coalesce(@p,f2) then 'true' else 'false' end

from @t

February 5, 2008 3:49 AM
 

Alex said:

As to Thomas' post: Thomas, there is not pitfall with coalesce per say, as null does not equal null. You would have to check to see if the values are equal ~or~ both null. (If you want 'true' if both values are null.)

April 25, 2008 3:16 PM
 

Tewr said:

I think that it is hard to compare your test with the tests of Lubarsky at http://blogs.x2line.com/al/archive/2004/03/01/189.aspx . In his case, the different methods are compared in a context; to use as a wrapper for optional parameters. Why does this matter? Well, there is a big difference between putting a constant as the second parameter in ISNULL or COALESCE compared to a table value (like in tbl.field = COALESCE(@myOptionalInputVar, tbl.field)). If the optional paramter is not set (=is NULL), the server has to read the second value each time, to compare it with itself.

When using a lot of optional parameters (in my case I have seven) there is a _very_ significant difference in speed between using ISNULL() Or COALESCE(), compared to the last method ((@myOptionalInputVar IS NULL) Or (tbl.field = @myOptionalInputVar)), as the expression is short-circuited when the first sub-expression evaluates to true. Try including these thoughts in your tests, and your conclusion about scarifying standards might be different. At least if you use this functions in an optional parameter context :)

November 19, 2008 10:22 AM
 

Tewr said:

Scarifying = sacrificing, hehe

November 19, 2008 10:24 AM
 

Nickywan said:

The fourth test is invalid in this script !

Because you use COALESCE instead of ISNULL at line 48

December 30, 2008 5:40 AM
 

Adam Machanic said:

Hi Nickywan,

Nice catch--funny that it took four years for someone to notice :-)

Interestingly, any difference appears to be gone in SQL Server 2008.  I just ran a fixed version of the script on both a 2005 and 2008 instance, on the same machine.  Following are the results:

2005

-----------

COALESCE, both non-null

Total CPU time: 39

Total milliseconds: 1423

ISNULL, both non-null

Total CPU time: 44

Total milliseconds: 1500

COALESCE, first column null

Total CPU time: 40

Total milliseconds: 1393

ISNULL, first column null

Total CPU time: 45

Total milliseconds: 1513

-----------

2008

-----------

COALESCE, both non-null

Total CPU time: 44

Total milliseconds: 1716

ISNULL, both non-null

Total CPU time: 47

Total milliseconds: 1720

COALESCE, first column null

Total CPU time: 41

Total milliseconds: 1626

ISNULL, first column null

Total CPU time: 48

Total milliseconds: 1733

-----------

December 31, 2008 9:46 AM
 

NULL ou 0 ? | hilpers said:

January 18, 2009 7:13 AM
 

Rahul said:

Hi Adam,

I am using ISNULL in the stored procedure in my sql server 2000. I am going to migrate to all to SQL server 2008. Will be there any problem? What we need to take care while migrating from 2000 to 2008 (stored procedures,tables,views,functions)

December 11, 2009 2:11 PM
 

Adam Machanic said:

Hi Rahul,

ISNULL is still very much supported in SQL Server 2008.

Before you upgrade, it is recommended that you run the SQL Server Upgrade Advisor tool which Microsoft created to help find potential problems. Google for more information.

December 11, 2009 3:25 PM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About Adam Machanic

Adam Machanic is a Boston-based independent database consultant, writer, and speaker. He has been involved in dozens of SQL Server implementations for both high-availability OLTP and large-scale data warehouse applications, and has optimized data access layer performance for several data-intensive applications. Adam has written for numerous web sites and magazines, including SQLblog, Simple Talk, Search SQL Server, SQL Server Professional, CoDe, and VSJ. He has also contributed to several books on SQL Server, including "Expert SQL Server 2005 Development" (Apress, 2007) and "Inside SQL Server 2005: Query Tuning and Optimization" (Microsoft Press, 2007). Adam regularly speaks at user groups, community events, and conferences on a variety of SQL Server and .NET-related topics. He is a Microsoft Most Valuable Professional (MVP) for SQL Server, a Microsoft Certified IT Professional (MCITP), and a member of the INETA North American Speakers Bureau.

This Blog

Syndication

News

Powered by Community Server (Commercial Edition), by Telligent Systems
  Privacy Statement