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():
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.
( 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:
( 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.
Want to use code from this post? Check out the license.