SQL Server - weird CASE bahavior - Simple Case vs Searched Case - sql-server

I recently ran into this pickle with the CASE-THEN-ELSE statement in SQL Server (2014 if it matters), to be more accurate, "the Simple" vs "the Searched" CASE expression. Until now, I thought that the only difference between these 2 is simply the format and/or the habit in writing both ways of the case expression but I guess I was completely wrong :)
MSDN Link
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.
Here is the example:
set nocount on
declare #test nvarchar(50) = null
select
#test as [The NULL Value],
case
when #test is null
then null
else 'Not Null???'
end as [As Expected],
case #test
when null
then null
else 'Not Null???'
end as [The Pickle]
And the result is:
The NULL Value As Expected The Pickle
-------------------------------------------------- ----------- -----------
NULL NULL Not Null???
Could someone provide a link to a MSDN documentation where this is explained, perhaps in a more detailed manner? :)
P.S.: I bet a lot of you folks were certain that both results would yield the same output :D

It's not weird at all ...
The "shortcut" way of
case #test
when null
then null
else 'Not Null???'
end as [The Pickle]
evaluates the variable/column (here: #test) against the values in the WHEN clauses (when null) with the regular equality operator - and comparing NULL using the standard equality operator (#test = null) is always undefined/NULL itself (standard SQL behavior), so it's not true
Therefore you are getting this result - Not Null??? - for your column The Pickle
If you want to check for NULL, you must use IS NULL like in your first example...

declare #t int =1
--simple case
select
case #t
when 1 then 1 else null end
The above query is expanded to below form..
select
case when #t=1 then 1 else null end
so a query with null will expand to below
declare #t int=null
select case #t
when null then null else 'notnull' end
is expanded to
select case when #t=null then null else 'notnull' end
which obviously evaluates to not null..
So in summary only in null case you will not get results you are expecting,try below to see
declare #t int=null
declare #t1 int =1
select
case when #t is null then null else 'notnull' end as 'Searchedcase',
case #t when null then null else 'notnull' end as'simple case',
case when #t1 =1 then 1 else null end as 'Searchedcase for value',
case #t1 when 1 then 1 else null end as'simple case for value'

See discussion of NULL and UNKNOWN from Transact-SQL reference to get a handle on why '=' doesn't work for NULL.
Simple CASE must be implicitly using '=', rather than IS NULL. So to make IS NULL explicit, use a Searched CASE expression.
Maybe Microsoft will add some functionality to the simple CASE expression to if NULL is encountered, then operator 'IS' is used?

Related

Filter Null value based on condition in SQL Server [duplicate]

Could someone please explain the following behavior in SQL?
SELECT * FROM MyTable WHERE MyColumn != NULL (0 Results)
SELECT * FROM MyTable WHERE MyColumn <> NULL (0 Results)
SELECT * FROM MyTable WHERE MyColumn IS NOT NULL (568 Results)
<> is Standard SQL-92; != is its equivalent. Both evaluate for values, which NULL is not -- NULL is a placeholder to say there is the absence of a value.
Which is why you can only use IS NULL/IS NOT NULL as predicates for such situations.
This behavior is not specific to SQL Server. All standards-compliant SQL dialects work the same way.
Note: To compare if your value is not null, you use IS NOT NULL, while to compare with not null value, you use <> 'YOUR_VALUE'. I can't say if my value equals or not equals to NULL, but I can say if my value is NULL or NOT NULL. I can compare if my value is something other than NULL.
NULL has no value, and so cannot be compared using the scalar value operators.
In other words, no value can ever be equal to (or not equal to) NULL because NULL has no value.
Hence, SQL has special IS NULL and IS NOT NULL predicates for dealing with NULL.
Note that this behavior is the default (ANSI) behavior.
If you:
SET ANSI_NULLS OFF
http://msdn.microsoft.com/en-us/library/ms188048.aspx
You'll get different results.
SET ANSI_NULLS OFF will apparently be going away in the future...
We use
SELECT * FROM MyTable WHERE ISNULL(MyColumn, ' ') = ' ';
to return all rows where MyColumn is NULL or all rows where MyColumn is an empty string. To many an "end user", the NULL vs. empty string issue is a distinction without a need and point of confusion.
In SQL, anything you evaluate / compute with NULL results into UNKNOWN
This is why SELECT * FROM MyTable WHERE MyColumn != NULL or SELECT * FROM MyTable WHERE MyColumn <> NULL gives you 0 results.
To provide a check for NULL values, isNull function is provided.
Moreover, you can use the IS operator as you used in the third query.
The only test for NULL is IS NULL or IS NOT NULL. Testing for equality is nonsensical because by definition one doesn't know what the value is.
Here is a wikipedia article to read:
https://en.wikipedia.org/wiki/Null_(SQL)
NULL Cannot be compared to any value using the comparison operators. NULL = NULL is false. Null is not a value. The IS operator is specially designed to handle NULL comparisons.
null represents no value or an unknown value. It doesn’t specify why there is no value, which can lead to some ambiguity.
Suppose you run a query like this:
SELECT *
FROM orders
WHERE delivered=ordered;
that is, you are looking for rows where the ordered and delivered dates are the same.
What is to be expected when one or both columns are null?
Because at least one of the dates is unknown, you cannot expect to say that the 2 dates are the same. This is also the case when both dates are unknown: how can they be the same if we don’t even know what they are?
For this reason, any expression treating null as a value must fail. In this case, it will not match. This is also the case if you try the following:
SELECT *
FROM orders
WHERE delivered<>ordered;
Again, how can we say that two values are not the same if we don’t know what they are.
SQL has a specific test for missing values:
IS NULL
Specifically it is not comparing values, but rather it seeks out missing values.
Finally, as regards the != operator, as far as I am aware, it is not actually in any of the standards, but it is very widely supported. It was added to make programmers from some languages feel more at home. Frankly, if a programmer has difficulty remembering what language they’re using, they’re off to a bad start.
I would like to suggest this code I made to find if there is a change in a value,
i being the new value and d being the old (although the order does not matter). For that matter, a change from value to null or vice versa is a change but from null to null is not (of course, from value to another value is a change but from value to the same it is not).
CREATE FUNCTION [dbo].[ufn_equal_with_nulls]
(
#i sql_variant,
#d sql_variant
)
RETURNS bit
AS
BEGIN
DECLARE #in bit = 0, #dn bit = 0
if #i is null set #in = 1
if #d is null set #dn = 1
if #in <> #dn
return 0
if #in = 1 and #dn = 1
return 1
if #in = 0 and #dn = 0 and #i = #d
return 1
return 0
END
To use this function, you can
declare #tmp table (a int, b int)
insert into #tmp values
(1,1),
(1,2),
(1,null),
(null,1),
(null,null)
---- in select ----
select *, [dbo].[ufn_equal_with_nulls](a,b) as [=] from #tmp
---- where equal ----
select *,'equal' as [Predicate] from #tmp where [dbo].[ufn_equal_with_nulls](a,b) = 1
---- where not equal ----
select *,'not equal' as [Predicate] from #tmp where [dbo].[ufn_equal_with_nulls](a,b) = 0
The results are:
---- in select ----
a b =
1 1 1
1 2 0
1 NULL 0
NULL 1 0
NULL NULL 1
---- where equal ----
1 1 equal
NULL NULL equal
---- where not equal ----
1 2 not equal
1 NULL not equal
NULL 1 not equal
The usage of sql_variant makes it compatible for variety of types
NULL is not anything...it is unknown. NULL does not equal anything. That is why you have to use the magic phrase IS NULL instead of = NULL in your SQL queries
You can refer this: http://weblogs.sqlteam.com/markc/archive/2009/06/08/60929.aspx

SQL CASE checking for two conditions from the same column inside stored procedure in sql server

SQL CASE checking for two conditions from the same column stored procedure sql server not working.
SELECT *
FROM TestTable
WHERE CASE WHEN ISNULL(column1,0)=0 THEN ISNULL(OfficeFlg,0)=1 ELSE ISNULL(column2,0)=1 END
I think you are looking for
WHERE (column1 IS NULL AND OfficeFlg IS NOT NULL)
OR
(column1 IS NOT NULL AND column2 IS NOT NULL)
Let's see your case and try to understand from it what you need
CASE WHEN ISNULL(column1,0)=0 THEN ISNULL(OfficeFlg,0)=1 ELSE ISNULL(column2,0=1) END
WHEN ISNULL(column1,0)=0 THEN ISNULL(OfficeFlg,0)=1 mean column1 IS NULL AND OfficeFlg IS NOT NULL.
ELSE ISNULL(column2,0=1) mean column1 IS NOT NULL AND column2 IS NOT NULL.
Or maybe (cause it's not clear what are you trying to do exactly)
WHERE (column1 IS NULL AND OfficeFlg =1 )
OR
(column1 IS NOT NULL AND column2 =1 )
You're trying to use a CASE expression like a CASE statement; that's not how they work.
CASE expressions return a Scalar value, not the result of a boolean expression. If I under your logic correctly, what you're after in Boolean Logic would be:
SELECT *
FROM TestTable
WHERE ((Column1 = 0 OR Column1 IS NULL)
AND OfficFlg = 1)
OR (Column1 != 0 AND Column2 = 1);
Notice I've removed the use of ISNULL and the CASE. Using either in your WHERE makes the query non-SARGable, which means performance could be severely effected. For things like ISNULL instead, use IS NULL and IS NOT NULL expressions and for CASE use boolean logic with ORs and ANDs.

T-SQL Variable in Where clause

I want to return the records in this case that have company ABC... and contact is null. It will not return anything when #Contact is null. Or any variable for that matter. Ideas?
DECLARE #Customer NVARCHAR(40) = 'ABC Company',
#Contact NVARCHAR(40) = NULL
SELECT
Company
FROM
company
WHERE
contact = #Contact AND customer = #Customer
Thanks,
EB
NULL is special in that it means UNKNOWN.
A known value (contact) can never equal an unknown value. You need an OR statement to check if it's equal OR is null
where (contact = #Contact OR (contact is null AND #Contact is null))
and customer = #Customer
Maybe something like that?
you can write
WHERE
ISNULL(contact,'') = ISNULL(#Contact,'') AND customer = #Customer
this do a null check and if null,then value will be considered as empty string for comparison.
instead of null==null (Which gives false), ''=='' will be performed.
if(null =null)
print 'Equal'
else
print 'not equal'
/*******************************************/
if('' ='')
print 'Equal'
else
print 'not equal'
In SQL, there is trivalent logic applied. In this reference you can read in detail about such logic. Bottom line is, that among true and false, there is another value: UNKNOWNN, which (in SQL) is the result of comparisons with NULL values. You can think of it as false (in this case).
Now, to visualise:
this queries won't return anything, as the where clause evaluates to UNKNOWN:
select 1 where null = 0
select 1 where null <> 0
select 1 where null = null
While this might be obvious, there's consequence: when you use not in operator.
When right operand contains NULLs, the query will return no records, e.g.:
select 1 where 0 not in (null, 1, 2)
won't return anything. It's especially important when you put some query as right operand.

Is it necessary to test for NULL if also testing for greater than?

I inherited some old stored procedures today, and came across several examples that followed this general pattern, where #Test is some INT value:
IF #Test IS NOT NULL AND #Test > 0
-- do something...
My understanding is that if #Test is NULL, then it has no value, and is not greater than, less than or even equal to zero. Therefore, testing for NULL is redundant in the above code:
IF #Test > 0
-- do something...
This second version seems to work just fine, and is far more readable IHMO.
So, my question: Is my understanding of NULL being unnecessary in this instance correct, or is there some obvious use-case I'm overlooking here where it could all go horribly wrong?
Note: In some cases, it was obvious that the intent was checking for the existence of a value, and I've changed those to IF EXISTS... my question is more concerned with the general case outlined above.
In SQL all comparisons to a NULL value evaluate to false.
So you always have to check explicitly for NULL, if you wish to act on it.
So, in this case, the additional test is not necessary.
#FlorianHeer is right on. NULL > 0 will eventually evaluate to false but as #Pred points out that is because Null > 0 actually evaluates to null and null cast to a bit is false....
A null is an unknown and therefore any comparison with it is also unknown. Think of arithmetic functions such as addition 1 + NULL = NULL, or concatenation 'A' + NULLL = NULL. NULL means the SQL database engine cannot interpret what its value is so any function or comparison on it is also unknown.
#MikkaRin pointed out that it is the assumption in the ELSE portion of a case statement or IF statement where that can become problematic but lets also think about this in the context of a join and how you may or may not want to see the results.
DECLARE #Table1 AS TABLE (Col INT)
DECLARE #Table2 AS TABLE (Col INT)
INSERT INTO #Table1 VALUES (1),(2),(3)
INSERT INTO #Table2 VALUES (1),(NULL),(3),(4)
SELECT *
FROM
#Table1 t1
INNER JOIN #Table2 t2
ON t1.Col <> t2.Col
Naturally you might think because NULL would be not equal to 1,2,3 that it should be included in the result set. But null is unknown so SQL is saying well I don't know if NULL could be 1,2,3 so I cannot return that as a result.
Now lets do the same thing but add a NULL in the first table:
DECLARE #Table1 AS TABLE (Col INT)
DECLARE #Table2 AS TABLE (Col INT)
INSERT INTO #Table1 VALUES (1),(2),(3),(NULL)
INSERT INTO #Table2 VALUES (1),(NULL),(3),(4)
SELECT *
FROM
#Table1 t1
INNER JOIN #Table2 t2
ON t1.Col = t2.Col
Again you might think that NULL is = to NULL but any comparison of NULL is considered unknown so even though both tables have NULL in it it will not be returned in the dataset.
Now consider:
DECLARE #Table1 AS TABLE (Col INT)
INSERT INTO #Table1 VALUES (1),(2),(3),(NULL)
SELECT *, CASE WHEN Col < 2 THEN Col ELSE 1000 END as ColCase
FROM
#Table1 t1
Which will make even the NULL 1000 the question is should NULL an unknown be 1000? if NULL is unknown how do we know that it isn't less than 2?
For a lot of your operations it may simply be enough to compare #Value > 1 but especially when you start dealing with ELSE in case of IF statements or joining on the antithesis you should consider dealing with the NULLs. Such as using ISNULL() or COALESCE() as #GuidoG points out.
IMHO being explicit about your intentions during operations to appropriately account for null values out weighs the minimal savings of typing.
Compare with NULL is necessary if you use ELSE statements:
for example:
declare #t int
set #t=null
if (#t>0) print '1' -- works fine
if (#t<0) print '2' --works fine
if (#t>0)
print '3' --works fine
else print '4' --here we start getting problems, because we are sure that #t<=0 that is obviously not true
you could replace it with
if isnull(#test, 0) > 0
This way it will be shorter and you still have checked everything
another interesting example:
SELECT (null > 0) AS a, !(null > 0) AS b
value of both a and b will be NULL
From my understanding, in some cases null checks are added sometimes to short circuit OR logic. For example, consider the following:
select * from tbl where (#id is null or #id > id)
If you pass in a value for #id, it tests the first condition (#id is null) and sees that it's false, but since it's part of an OR statement, it then goes ahead and then runs the #id > id comparison to see what that one returns as well. OR statements only need one true returned for the whole thing to resolve to true, and must keep testing until it comes across an OR condition that does.
Whereas if you pass in null for the #id parameter, as soon as it gets to the first condition and it returns true. Seeing that the next it's part of an OR statement, SQL knows it doesn't even have to do any of the following comparison, because the entire OR statement has already resolved to true. The #id > id comparison and will not even run it. This can save a ton of processing if it's a huge table or complex join, etc.

Case Statement in Select Not Working

This is for SQL Server 2012 database...
I'm reading a varchar data column from a table, and depending on user-selected options the data could be either alphanumeric or numeric. I need to sort by this column so I'm trying to use a case statement, but it doesn't seem to be working. Below is a simplified example of what I'm doing, but as you can see, it's falling through to the Else of the case statement in both scenarios...any ideas on what I'm doing wrong?
Select '1st Grade Math' topic Into #temp
Declare #rptView int
Set #rptView = 1
Select Case #rptView
When 1 Then topic
Else cast(topic as int)
End
From #temp
Order by Case #rptView
When 1 Then topic
Else cast(topic as int)
End
Select Case
When #rptView = 1 Then topic
Else cast(topic as int)
End
From #temp
Order by Case #rptView
When 1 Then topic
Else cast(topic as int)
End
drop table #temp
Consider the following example based on your table:
Select Case 1 When 1 Then topic
Else 5
End
From #temp
It also fails with the following error:
Conversion failed when converting the varchar value '1st Grade Math' to data type int.
Why? Because every expression must have a well-defined data type. SQL Server deduces that the type of your first column is int, since the ELSE clause contains an int. Thus, it tries to convert topic to int as well, which fails.
In other words: You can't do it like that. The field in your result set can be varchar or int, not both.
Adding few more examples which may help..
declare #a int=1
declare #b varchar='b'
--this works
select
case when #a=1 then #a else #b end
--this also works
select
case when #a=2 then #b else #b end
--this fails
select
case when #a=1 then #b else #a end
--this fails
select
case when #a=2 then #a else #b end
Why ..?Because of data type precedence ,SQL tries to convert everything to type with higher precedence

Resources