SQL ISZERO() And NULLIF() For Dividing By Zero

Posted January 15, 2007 at 3:23 PM

Tags: SQL

I am working on writing a bunch of SQL reports that have a lot of SQL aggregates. One of the computations that comes up a lot is the figuring out of percentages. To this, as you all know, you have to divide one number by another number and of course the number on the bottom cannot be Zero.

Since many of these bottom numbers are aggregates (thing SQL SUM(), MAX(), MIN()), I don't want to call them too often. What I want is something that works like the SQL function ISNULL(), where you can pass in a value just once. So, I wrote a quick little function called ISZERO():

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

  • CREATE FUNCTION IsZero (
  • @Number FLOAT,
  • @IsZeroNumber FLOAT
  • )
  • RETURNS FLOAT
  • AS
  • BEGIN
  •  
  • IF (@Number = 0)
  • BEGIN
  • SET @Number = @IsZeroNumber
  • END
  •  
  • RETURN (@Number)
  •  
  • END

This simply checks the passed in number to see if it Zero. If it is, then I just pass back the alternate number. If is not Zero, then I pass back the original value. This allows me to pass in computational-heavy numbers without having to compute them more than once.

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

  • (
  • CAST( SUM( r.price ) AS FLOAT ) /
  • (
  • SELECT
  • dbo.ISZERO( SUM( r2.price ), 1 )
  • FROM
  • reward r2
  • INNER JOIN
  • [order] o2
  • ON
  • r2.order_id = o2.id
  • WHERE
  • o2.user_id = u.id
  • ) *
  • 100
  • ) AS percentage_used

Notice that I only have to run the SUM() aggregate once (for each value).

What's also nice about this is that it converts the passed-in numbers to FLOAT automatically which saves me having to do it on the other end of things. This is great as all of the reporting values require FLOAT values.

After I wrote this, it occurred to me that this is probably a VERY common problem. So, I did some Google searches to see how other people have dealt with this. Doing so, I came across a very cool function named NULLIF(). This function returns a NULL if the two passed-in arguments are the same value. That can be super useful.

You could accomplish the ISZERO() method using NULLIF() this way:

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

  • (
  • CAST( SUM( r.price ) AS FLOAT ) /
  • ISNULL(
  • NULLIF(
  • (
  • SELECT
  • SUM( r2.price )
  • FROM
  • reward r2
  • INNER JOIN
  • [order] o2
  • ON
  • r2.order_id = o2.id
  • WHERE
  • o2.user_id = u.id
  • ),
  • 0
  • ),
  • 1
  • ) *
  • 100
  • ) AS percentage_used

This works fine, but is just a bit too wordy for my taste. But certainly, NULLIF() is a great function to have under the belt.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page



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

Reader Comments

Jan 15, 2007 at 3:37 PM // reply »
42 Comments

I do it the lazy mans way:

CASE when denominator = 0
then 0
else numerator/denominator end as result

Did I use those terms correctly? I suck at math, but I think you get the picture.


Peter
Jan 15, 2007 at 4:54 PM // reply »
1 Comments

Or, you can take the REALLY lazy way out:

<cfif not isNumeric(qryGraphData_total.t_avg)>
<cfset t_avg = 0>
<cfelse>
<cfset t_avg = qryGraphData_total.t_avg>
</cfif>


Peter
Jan 15, 2007 at 4:54 PM // reply »
6 Comments

Or, you can take the REALLY lazy way out:

<cfif not isNumeric(qryGraphData_total.t_avg)>
<cfset t_avg = 0>
<cfelse>
<cfset t_avg = qryGraphData_total.t_avg>
</cfif>


Jan 16, 2007 at 7:32 AM // reply »
5,406 Comments

@Todd,

That is how I would do it if the value that I was testing was a simple value, but in my case, I was testing an aggregate which has a lot of processing to it:

CASE
WHEN SUM( r.price ) = 0
THEN 1
ELSE SUM( r.price )
END

I would have to run the SUM() aggregate twice which is what I want to avoid doing.

@Peter,

That is how I would do it in ColdFusion, but I needed to do all this in the actual SQL statement as I needed to pass this query result off to a report generator. I could have updated the query after it was executed (which is actually what I was doing originally), but then I felt that I could accomplish the same thing faster by tightening up my SQL.


Post Comment  |  Ask Ben

Recent Blog Comments
Justice
Jul 3, 2009 at 11:10 PM
Create A Running Average Without Storing Individual Values
@Ben, I think you're going about this the wrong way. You're trying to use complicated techniques when there is a simple and beautiful technique readily available (a la Gary Funk's comment). Instead ... read »
Bob
Jul 3, 2009 at 9:19 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 3 / Week 1
a good technical explanation http://crossfitphoenix.typepad.com/crossfit_phoenix_forging_/the-overhead-squat.html ... read »
Jul 3, 2009 at 9:03 PM
Create A Running Average Without Storing Individual Values
If I wanted to do this and only carry two numbers, I'd keep track of the sum and N. Then you are pretty much accurate all the time. average = (sum + new_number) / (N + 1) But all this was in a for ... read »
Roland Collins
Jul 3, 2009 at 8:58 PM
Create A Running Average Without Storing Individual Values
@Martin - not just floating point though. Depending on what langauge you're working in, decimals can cause just as many headaches if they're not precise enough. But again, for most applications, th ... read »
Isnogood
Jul 3, 2009 at 7:16 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 3 / Week 1
Watch this http://www.nsca-lift.org/videos/default.shtml ... read »
Aaron
Jul 3, 2009 at 7:13 PM
Project HUGE: Get Big, Phase One (Chat Waterbury - Huge In A Hurry)
I've just finished the 3rd week of phase 3, and have to agree that the overhead squats are hard. I think this is most due to the wide grip on which places more pressure on your upper back. Only this ... read »
Isnogood
Jul 3, 2009 at 7:11 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 3 / Week 1
Very good, there were some near perfect reps, and there were some dodgy ones, but you're getting there your body position is good. Work on your depth and do not let the bar move forward or backward, ... read »
Martin Mädler
Jul 3, 2009 at 6:48 PM
Create A Running Average Without Storing Individual Values
Nice dodge! I dig the idea to force out the last bit of performance out of a chunk of code, even though it's such a minor thing. Heard of this kinda approach in connection with "running sums". @Rola ... read »