CASE Expression with T-SQL - sql-server

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.

Related

CASE Statement SQL: Priority in cases?

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.

SQL Server CASE 1 WHEN 1, WHERE 1=1 AND 1=1

I inherited the following query from a previous application. I'm having a hard time understanding the "Case" in the "Select" and "Where" clause also.
SELECT J1.AC_CODE, J1.PERIOD, J1.JRNAL_NO, J1.DESCRIPTN, - J1.AMOUNT ,
J1.ANAL_T3,
CASE 1
WHEN 1 THEN 'A'
ELSE J1.ACCNT_CODE
END ,
J1.JRNAL_LINE
FROM dbo.JSource J1
WHERE 1=1
AND 1=1
AND NOT ('A' LIKE '%Z%'
AND J1.JRNAL_SRCE IN ('B/F',
'CLRDN')
AND J1.JRNAL_NO = 0)
AND CASE 1
WHEN 1 THEN 'A'
ELSE J1.AC_CODE
END ='A'
AND J1.AC_CODE='156320'
AND J1.PERIOD BETWEEN 2014001 AND 2014012
AND J1.ANAL_T3='ANAL001'
ORDER BY 1,2,3,4,5,6,7,8
I'm not sure If I understand the following clauses correctly:
1st Clause:
CASE 1
WHEN 1 THEN 'A'
ELSE J1.AC_CODE
END
I understood as: If column 1 is true, then choose literal A ortherwise choose J1.AC_CODE.
2nd clause:
WHERE 1=1
AND 1=1
AND NOT ('A' LIKE '%Z%'
AND J1.JRNAL_SRCE IN ('B/F',
'CLRDN')
AND J1.JRNAL_NO = 0)
AND CASE 1
WHEN 1 THEN 'A'
ELSE J1.AC_CODE
END ='A'
AND J1.AC_CODE='156320'
AND J1.PERIOD BETWEEN 2014001 AND 2014012
AND J1.ANAL_T3='ANAL001'
I'm totally lost with this "Where" clause.
Can you help explain this query and write a better version for this whole query?
I'm running this query on SQL Server 2008 (R2)
I understood as: If column 1 is true, then choose literal A ortherwise
choose J1.AC_CODE.
No, it is comparing the value 1 with the value 1 and if that is true the case returns an A and that is of course always true so the case statement will always return A.
Your where clause does not do anything at all.
1=1
AND 1=1
will always be true and the case will always be true and 'A' LIKE '%Z%' will always be false and that makes the entire AND NOT 'A' LIKE '%Z%' .... expression to always be true.
A simpler version of your query would look like this.
SELECT J1.AC_CODE,
J1.PERIOD,
J1.JRNAL_NO,
J1.DESCRIPTN,
- J1.AMOUNT,
J1.ANAL_T3,
'A',
J1.JRNAL_LINE
FROM dbo.JSource J1
WHERE J1.AC_CODE='156320' AND
J1.PERIOD BETWEEN 2014001 AND 2014012 AND
J1.ANAL_T3='ANAL001'
ORDER BY 1,2,3,4,5,6,7,8
Without knowing the history of this query, I am guessing that this was written with testing/debugging in mind and some of that code has been left in place. The case statement in the select line could (and I repeat could as this is my guess from looking at the query) have had other with clauses during creation of the query used for testing and these would have been switched between by changing the value after the CASE (example SELECT ..... CASE 1 WHEN 1 THEN 'A' WHEN 2 THEN 'some value' WHEN 3 'some other value' ELSE J1.ACCNT_CODE).
As for the where 1 = 1, I have seen this used during query creation/testing - mainly because it means each of the true conditions can easily be commented/uncommented or cut & pasted as the first where condition is always true. I've not seen AND 1 = 1 before. Not sure what that line was intended for, but I'd still think came about from testing/debugging and was not taken out the query.

T-SQL CASE Statement without overlapping criteria test

Since the following code prints 'First' and 'Second' INSERT that order, can I conclude that the first condition satisfied is ALWAYS executed?
DECLARE #Constant1 int = 1
DECLARE #Constant2 int = 2
select
case
when #Constant1 = 1
then 'First'
when #Constant1 = 1 and #Constant2 = 2
then 'Second'
end as Result
select
case
when #Constant1 = 1 and #Constant2 = 2
then 'Second'
when #Constant1 = 1
then 'First'
end as Result
I know that sometimes parallel processing effects the outcome and I was trying to understand IF this type of situation that I see in Production would always return the same result.
This question is intended to understand if there is a potential issue in production code. If I were going to write the code anew, I think I would try to make the code explicitly mutually exclusive..
select
case
when #Constant1 = 1 and #Constant2 != 2
then 'First'
when #Constant1 = 1 and #Constant2 = 2
then 'Second'
end as Result
The Documentation for CASE states.
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.
So it will return the first true branch.
For a simple query such as in the question I would expect it to not evaluate the other branches either.
A few cases where this short circuiting behaviour does not work as expected/advertised are discussed in this DBA site question.
Does SQL Server read all of a COALESCE function even if the first argument is not NULL?
But just to be clear these issues do not affect the left to right precedence order of the result (except for the case when evaluating a later branch causes an error to occur such that no result is returned at all)

Is there an Alternate for Where is Null using Where = Null?

If not, is there an alternate way to switch through SELECT statements using a CASE or IF/THEN identifier WITHOUT putting the statement in a scalar variable first?
Is there a way to format this without using IS and using an = sign for it to work?
SELECT ID FROM TABLE WHERE ID = Null
No. NULL isn't a value. Think of NULL as a condition, with IS NULL or IS NOT NULL is testing for this condition.
In this example you can test for the actual value, or lack of value represented by a conditon
WHERE
(X IS NULL OR X = #X)
OR
WHERE
(#X IS NULL OR X = #X)
Or test for your definite conditions first:
WHERE
CASE X
WHEN 1 THEN
WHEN 2 THEN
ELSE -- includes NULL
END = ...
Your question is abstract so hard to give a more precise answer.
For example, are you having problems with NOT IN and NULL? If so, use NOT EXISTS.

Microsoft SQL: CASE WHEN vs ISNULL/NULLIF

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]

Resources