Besides readability is there any significant benifit to using a CASE WHEN statement vs ISNULL/NULLIF when guarding against a divide by 0 error in SQL?
CASE WHEN (BeginningQuantity + BAdjustedQuantity)=0 THEN 0
ELSE EndingQuantity/(BeginningQuantity + BAdjustedQuantity) END
vs
ISNULL((EndingQuantity)/NULLIF(BeginningQuantity + BAdjustedQuantity,0),0)
Remember that NULL is different from 0. So the two code snippets in the question can return different results for the same input.
For example, if BeginningQuantity is NULL, the first expression evaluates to NULL:
CASE WHEN (NULL + ?)=0 THEN 0 ELSE ?/(NULL + ?) END
Now (NULL + ?) equals NULL, and NULL=0 is false, so the ELSE clause is evaluated, giving ?/(NULL+?), which results in NULL. However, the second expression becomes:
ISNULL((?)/NULLIF(NULL + ?,0),0)
Here NULL+? becomes NULL, and because NULL is not equal to 0, the NULLIF returns the first expression, which is NULL. The outer ISNULL catches this and returns 0.
So, make up your mind: are you guarding against divison by zero, or divison by NULL? ;-)
In your example I think the performance is negligible. But in other cases, depending on the complexity of your divisor, the answer is 'it depends'.
Here is an interesting blog on the topic:
For readability, I like the Case/When.
In my opinion, using Isnull/Nullif is faster than using Case When. I rather the isnull/nullif.
I would use the ISNULL, but try to format it so it shows the meaning better:
SELECT
x.zzz
,x.yyyy
,ISNULL(
EndingQuantity / NULLIF(BeginningQuantity+BAdjustedQuantity,0)
,0)
,x.aaa
FROM xxxx...
CASE WHEN (coalesce(BeginningQuantity,0) + coalesce(BAdjustedQuantity,0))=0 THEN 0 ELSE coalesce(EndingQuantity,0)/(coalesce(BeginningQuantity,0) + coalesce(BAdjustedQuantity,0)) END
your best option imho
Sorry, here is the little more simplify upbuilded sql query.
SELECT
(ISNULL([k1],0) + ISNULL([k2],0)) /
CASE WHEN (
(
CASE WHEN [k1] IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN [k2] IS NOT NULL THEN 1 ELSE 0 END
) > 0 )
THEN
(
CASE WHEN [k1] IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN [k2] IS NOT NULL THEN 1 ELSE 0 END
)
ELSE 1 END
FROM dbo.[Table]
Related
I need to compare variable in case condition in stored procedure.
case when #column <> 0 then ...
ELSE 0 END
but whenever am getting #column as NULL, then the above script returning as 0.
will sql consider both 0 & NULL as same.
Am using sql server 2014.
Thanks All
No. SQL considers NULL as "I have no idea". Comparing anything with "I have no idea" results in an answer of "I totally have no idea". Look:
- How high is John?
- I have no idea.
- What is two centimeters higher than John?
- I have no idea.
Even comparison between two NULL values is not true: if I have no idea how tall John is and if I also have no idea how tall Jack is, I can't conclude that John is equally tall as Jack (and I can't conclude that John is not equally tall as Jack). The only sensible answer is... "I have no idea".
The way to test for NULL is with IS operator, which specifically exists for this scenario (e.g. #column IS NULL, or #column IS NOT NULL).
So NULL is not equal to 0, nor is it NOT equal to 0. The result of NULL <> 0 is NULL. However, NULL is falsy where conditionals are concerned, so CASE thinks you should get the ELSE branch any time #column is NULL.
In case if you want to execute the then part of case if the column value is null, then modify your condition to check for nulls also
CASE WHEN (#column <> 0 OR #column IS NULL) then ...
ELSE 0 END
When you run the following query in SQL Management studio the result will be 1.
SELECT
CASE WHEN '-' = 0 THEN
1
ELSE
0
END
That scares me a bit, because I have to check for 0 value a numerous number of times and it seems it is vulnerable for being equal to value '-'.
You're looking at it the wrong way around.
'-' is a string, so it will get implicitly casted to an integer value when comparing it with an integer:
select cast('-' as int) -- outputs 0
To make sure that you are actually comparing a value to the string '0', make your comparison like this instead:
select case when '-' = '0' then 1 else 0 end
In general, you're asking for trouble when you're comparing values of different data types, since implicit conversions happen behind the scene - so avoid it at all costs.
I'm in the process of creating some reports that take a finite total (lets say 2,500) of products (for this example lets say Ice Cream Cones) and counts how many of them were broken before serving.
Now the actual count code of broken cones I've got down.
SELECT COUNT(broken_cones) FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'
However, I need a percentage of broken cones from this total as well. I've been playing around with the code but I keep running into a 'Divide By Zero' error with this code below.
SELECT CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS
decimal(7,4)) FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
For right now, there aren't any broken cones (and won't be for a while) so the total right now is zero. How can I show the NULL scenario as zero?
I tried to place an ISNULL statement in the mix but I kept getting the 'Divide by Zero' error. Am I even doing this right?
::edit::
Here's what I ended up with.
SELECT
CASE
WHEN COUNT(broken_cones) = 0 then 0
ELSE CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS decimal(7,4))
END
FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
Use a case statement.
SELECT
CASE WHEN COUNT(broken_cones) = 0 then 0
ELSE CAST(NULLIF((.01 * 2500)/Count(broken_cones), 0) AS decimal(7,4)) END
FROM [ice].[ice_cream_inventory] WHERE broken_cones = 'Yes'
You already have a solution, but this is why your original solution didn't work.
Your NULLIF needs to be moved in order to be effective. It is doing the division before it gets to the NULLIF call. Dividing by null will return a null value.
SELECT CAST((.01 * 2500)/NULLIF(Count(broken_cones), 0) AS decimal(7,4))
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'`
The NULLIF() function is a great way to prevent divide by zero, since anything divide by NULL returns null. The way to use it is as follows:
<expression> / NULLIF( <expression>, 0 )
Unfortunately you've wrapped your whole divide expression in NULLIF() which is why it isn't working for you. So step one is to get it to return NULL if your COUNT() comes back zero:
SELECT
(0.01 * 2500) / NULLIF( COUNT(broken_cones), 0 )
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'
Now you said you wanted that NULL to come back zero? That is where you use ISNULL():
ISNULL(<expression1>, <expression2>)
If the first expression is NULL then return the second expression, so our SQL now becomes:
SELECT
ISNULL(
(0.01 * 2500) / NULLIF( COUNT(broken_cones), 0 ),
0
)
FROM [ice].[ice_cream_inventory]
WHERE broken_cones = 'Yes'
I have a general question for when you are using a CASE statement in SQL (Server 2008), and more than one of your WHEN conditions are true but the resulting flag is to be different.
This is hypothetical example but may be transferable when applying checks across multiple columns to classify data in rows. The output of the code below is dependant on how the cases are ordered, as both are true.
DECLARE #TESTSTRING varchar(5)
SET #TESTSTRING = 'hello'
SELECT CASE
WHEN #TESTSTRING = 'hello' THEN '0'
WHEN #TESTSTRING <> 'hi' THEN '1'
ELSE 'N/A'
END AS [Output]
In general, would it be considered bad practice to create flags in this way? Would a WHERE, OR statement be better?
Case statements are guaranteed to be evaluated in the order they are written. The first matching value is used. So, for your example, the value 0 would be returned.
This is clearly described in the documentation:
Searched CASE expression:
Evaluates, in the order specified, Boolean_expression for each WHEN clause.
Returns result_expression of the first Boolean_expression that evaluates to TRUE.
If no Boolean_expression evaluates to TRUE, the Database Engine returns the else_result_expression if an ELSE clause is specified, or
a NULL value if no ELSE clause is specified.
As for whether this is good or bad practice, I would lean on the side of neutrality. This is ANSI behavior so you can depend on it, and in some cases it is quite useful:
select (case when val < 10 then 'Less than 10'
when val < 100 then 'Between 10 and 100'
when val < 1000 then 'Between 100 and 1000'
else 'More than 1000' -- or NULL
end) as MyGroup
To conclude further - SQL will stop reading the rest of the of the case/when statement when one of the WHEN clauses is TRUE. Example:
SELECT
CASE
WHEN 3 = 3 THEN 3
WHEN 4 = 4 THEN 4
ELSE NULL
END AS test
This statement returns 3 since this is the first WHEN clause to return a TRUE, even though the following statement is also a TRUE.
I am using:
SELECT
CASE SR.[ContainerId] WHEN SR.[ContainerId] IS NULL
THEN 0
ELSE 1
END AS [IsSampleReceived]
FROM SomeTable SR where SomeCondition
Its not giving me the desired result. IsSampleReceived is always 1. I don't know why, maybe there's some thing wrong in WHEN SR.[ContainerId] IS NULL.
There are two formats of using CASE and you are mixing them together.
The CASE expression has two formats:
The simple CASE expression compares an expression to a set of simple
expressions to determine the result.
The searched CASE expression evaluates a set of Boolean expressions to
determine the result.
See http://msdn.microsoft.com/en-us/library/ms181765.aspx
Instead, try:
select case
when SR.[ContainerId] is null
then 0
else 1
end as [IsSampleReceived]
from SomeTable SR
where SomeCondition
Nearly! You were mixing the two different syntax forms:
SELECT CASE WHEN SR.[ContainerId] IS NULL THEN 0 ELSE 1 END AS [IsSampleReceived]
FROM SomeTable SR where SomeCondition
There are two kinds of CASE statements: "simple" and "searched". In your code, you are combining these two kinds of statements, and that is giving you incorrect results.
Simple:
SELECT CASE SR.[ContainerId] WHEN NULL THEN 0 ELSE 1 END AS [IsSampleReceived]
FROM SomeTable SR where SomeCondition
Searched:
SELECT CASE WHEN SR.[ContainerId] IS NULL THEN 0 ELSE 1 END AS [IsSampleReceived]
FROM SomeTable SR where SomeCondition
In your original query, it is doing a simple version comparing the value of SR.[ContainerId] to the value of SR.[ContainerId] IS NULL. The result of that comparison will always be false, so the else condition was always selected.