Comparing empty string with null value - SQL Server - sql

I need to compare two columns, and am hitting issues when comparing NULL against an empty string. Put simply, this is what I'm looking at.
DECLARE #EmptyString VARCHAR(20) = '', -- Could have valid VARCHAR data, empty string, or NULL
#Null VARCHAR(20) = Null; -- Could have valid VARCHAR data, empty string, or NULL
SELECT
CASE WHEN #EmptyString != #Null THEN 'Pass' ELSE 'Fail' END AS EmptyStringVsNull
In this case, because we all know that an empty string and a null column value are different, I would hope to see the result come back as 'Pass', however it doesn't.
From Martin's asnwer here, he explains that that a comparison like this results to UNKNOWN, rather than TRUE or FALSE, which clarifies the reason I am seeing these results, but I need find a solution to this. I know there must be a simply way around this that I'm missing...
I know that there are a few built in functions such as ISNULL() and NULLIF(), however I don't know if these can help in this situation...
ISNULL()
If we use the ISNULL() function to set null values to an empty string, then the comparison wont work as an empty string is equal to an empty string, for example
DECLARE #EmptyString VARCHAR(20) = '',
#Null VARCHAR(20) = Null;
SELECT
CASE WHEN ISNULL(#EmptyString, '') != ISNULL(#Null, '') THEN 'Pass' ELSE 'Fail' END AS EmptyStringVsNull
This also returns 'FAIL', so this is a no go. I could always use ISNULL to convert this to a different string, but this still isn't suitable, as the empty string may have a different value which by chance could match whatever we decide to convert null values to.
NULLIF()
DECLARE #EmptyString VARCHAR(20) = '',
#Null VARCHAR(20) = Null;
SELECT
CASE WHEN NULLIF(#EmptyString, '') != NULLIF(#Null, '') THEN 'Pass' ELSE 'Fail' END AS EmptyStringVsNull
If we use the NULLIF() function to convert empty strings to null, our comparison still doesn't return true. This is because, as explained in the linked post, comparing null values results in UNKNOWN.
With the simple SQL example above, how can I check that a NULL value is not equal to an empty string?

Your first example returns fail because you have the wrong operator. If you want to see if something equals something else you use =, not !=
Here is the code that proves that NULL can be compared to '':
DECLARE #EmptyString VARCHAR(20) = '',
#Null VARCHAR(20) = Null;
SELECT
CASE WHEN ISNULL(#EmptyString, '') = ISNULL(#Null, '')
THEN 'Pass' ELSE 'Fail'
END AS EmptyStringVsNull
It returns pass because you use =, not !=

Just reverse the comparison:
SELECT (CASE WHEN #EmptyString = #Null THEN 'Fail' ELSE 'Pass' END) as EmptyStringVsNull
The only complication is if you want two NULL values to be the same. If so:
SELECT (CASE WHEN #EmptyString = #Null OR (#EmptyString IS NULL AND #Null IS NULL)
THEN 'Fail' ELSE 'Pass'
END) as EmptyStringVsNull

DECLARE #EmptyString VARCHAR(20) = '', -- Could have VARCHAR data, empty string, or NULL
#Null VARCHAR(20) = Null; -- Could have VARCHAR data, empty string, or NULL
SELECT
CASE WHEN #EmptyString IS NOT #Null THEN 'Pass' ELSE 'Fail' END AS EmptyStringVsNull.
Because of ansi sql standard you can check for NUll value using IS NULL or IS NOT NULL operator.

It is quicker and more "Pythonic" (easier to understand by someone else who reads your code) to evaluate to the same outcome on both sides of the equal sign:
DECLARE #EmptyString VARCHAR(20) = ''
SELECT
CASE WHEN ISNULL(#EmptyString, '') != ''
THEN 'Pass'
ELSE 'Fail' END AS EmptyStringVsNull
GO
Both an EMPTY string and a true NULL value will yield TRUE for the search condition.
NOTE: It is not optimal, since good bussiness rules practices should clearly differenciate a NULL attribute from an EMPTY attribute.
i.e.
spouse_field = NULL (I still don't have the information regarding entity X's marital status)
spouse_field = '' (empty): That specific entity is confirmed as single. Otherwise, a name would have shown up.

Related

SQL COALESCE and IS NULL are not returning a space when when the query returns NULL

I am trying to optimize a humongous SQL query that was written by a self taught developer that used a ton of functions instead of JOINS. Anyway, I am having trouble displaying a space or a empty string('') when there is no value in the field selected. I've included only the SELECT in question. I am having the weirdest problem or just overlooking the correct answer in troubleshooting. Whenever I use COALESCE, when the field is supposed to be a blank string, it displays a zero. And when I use IS NULL, I get back NULL. All info online seems to point toward using COALESCE(value, '') as depicted in the code. But I am getting a 0 instead of ''. Does anyone see what I'm doing wrong? I'm using SSMS.
SELECT
pss8.dbo.xml_StripIllegalChars(dbo.rpt_get_series_volume(b.bookkey)) AS p_seriesvol --SELECT to be replaced that works but is slow due to function use I am told
,COALESCE(bd.seriesvolume, '') AS p_seriesvol --my SELECT that won't work!
FROM
bookdetail bd
WHERE
--bd.bookkey='303177'
bd.bookkey='6002'
The bookkeys at the bottom are for testing as I know the top one returns a 1 and the bottom one returns a '' previously when it worked. The SELECT above my commented SELECT is the code that works but is slow... According to what I read online, I am saying 'if there isn't a series volume number, then it equals an empty string.' Does COALESCE not work like this? Can it only return a 0 if the field has no value, or in this case, has no volume number? All help much appreciated. I'm very curious to hear a solution!
Here's more intel. This is how the this SELECT works:
pss8.dbo.xml_StripIllegalChars(dbo.rpt_get_series_volume(b.bookkey)) AS p_seriesvol
The
.rpt_get_series_vol
function manages to create an empty string with this code... Does this reveal anything?
DECLARE #RETURN
VARCHAR(5)
DECLARE #v_desc
VARCHAR(5)
DECLARE #i_volumenumber INT
SELECT #i_volumenumber = volumenumber
FROM bookdetail
WHERE bookkey = #i_bookkey and volumenumber <> 0
IF #i_volumenumber > 0
BEGIN
SELECT #RETURN = CAST(#i_volumenumber as varchar(5))
END
ELSE
BEGIN
SELECT #RETURN = ''
END
RETURN #RETURN
END
As you are looking for a '0' not a NULL COALESCE()is not useful, instead use a simple CASE:
select
...,
case bd.seriesvolume when '0' then '' else bd.seriesvolume end as p_seriesvol
from
...
Or if you want '' for 0 or NULL
case when bd.seriesvolume is null or bd.seriesvolume = '0' then '' else bd.seriesvolume end as p_seriesvo
COALESCE() function returns the 1st non null value
SELECT COALESCE(NULL, NULL, 'third_value', 'fourth_value'); returns the third value because the third value is the first value that is not null.
So in your case COALESCE(bd.seriesvolume, '') AS p_seriesvol if seriesvolume colum value is null then it will return blank string

SQL Server Compare to NULL

I have a lot of comparisons that I need to make between a value and its previous value.
For Example: ReceivedBy and PreviousReceivedBy.
I started with:
WHERE ReceivedBy != PreviousReceivedBy
But if either value is null then this returns false, (when I really need it to be true). So I updated it to look like this:
WHERE ReceivedBy != PreviousReceivedBy
OR (ReceivedBy IS NULL AND PreviousReceivedBy IS NOT NULL)
OR (ReceivedBy IS NOT NULL AND PreviousReceivedBy IS NULL)
This works fine, but I have a large list of fields that need to be compared. I would like to find a way to make this comparison with less code (without turning off ANSI_NULLS).
Obviously if there is no other way, then I will just put in all 3 lines for the comparison.
UPDATE:
As an example, here is what I am hoping for
ReceivedBy = 123
PreviousReceivedBy = 123
Result = FALSE
ReceivedBy = 5
PreviousReceivedBy = 123
Result = TRUE
ReceivedBy = NULL
PreviousReceivedBy = 123
Result = TRUE
ReceivedBy = 123
PreviousReceivedBy = NULL
Result = TRUE
ReceivedBy = NULL
PreviousReceivedBy = NULL
Result = FALSE
If both columns are varchars, I'd go with something like this:
coalesce(ReceivedBy, 'NULL') != coalesce(PreviousReceivedBy, 'NULL')
If they are integers, I'd put some values greatly below zero (to distinctly represent null value) instead of 'NULL'.
From names of columns I assume it has to be wether string value or integer value :)
UPDATE
As #Siyual pointed out, replacement string should be "out of the realm of possibility", you should replace 'NULL' above with some non-alphabetical character, as '#' :)
I encountered the same problem with you when taking comparison with nullable value, NULL always returns unknown as far away of our desired only between TRUE or FALSE
I ended up with declare a Scalar-valued functions with these logics like other SQL(s) dealing with null as
Ordinary comparison operators yield null (signifying "unknown"), not
true or false, when either input is null. For example, 7 = NULL yields
null, as does 7 <> NULL. When this behavior is not suitable, use the
IS [ NOT ] DISTINCT FROM constructs:
a IS DISTINCT FROM b => a != b
a IS NOT DISTINCT FROM b => a == b
Which a IS NOT DISTINCT FROM b could be rewritten as
(a IS NOT NULL AND b IS NOT NULL AND a=b) OR (a IS NULL AND b is NULL)
I use sql_variant for these basic parameters: int, datetime, varchar,...
create function IsEqual(
#a sql_variant,
#b sql_variant
)
returns bit
as
begin
return (CASE WHEN (#a IS NOT NULL AND #b IS NOT NULL AND #a=#b) OR (#a IS NULL AND #b is NULL) THEN 1 ELSE 0 END);
end
create function IsNotEqual(
#a sql_variant,
#b sql_variant
)
returns bit
as
begin
return 1-dbo.IsEqual(#a,#b);
end
To use
select dbo.IsEqual(null, null) Null_IsEqual_Null,
dbo.IsEqual(null, 1) Null_IsEqual_1,
dbo.IsEqual(1, null) _1_IsEqual_Null,
dbo.IsEqual(1, 1) _1_IsEqual_1,
dbo.IsEqual(CAST('2017-08-25' AS datetime), null) Date_IsEqual_Null,
dbo.IsEqual(CAST('2017-08-25' AS datetime), CAST('2017-08-25' AS datetime)) Date_IsEqual_Date
For your cases
select dbo.IsNotEqual(123,123) _123_IsNotEqual_123,
dbo.IsNotEqual(5,123) _5_IsNotEqual_123,
dbo.IsNotEqual(Null,123) Null_IsNotEqual_123,
dbo.IsNotEqual(123,Null) _123_IsNotEqual_Null,
dbo.IsNotEqual(Null,Null) Null_IsNotEqual_Null
Another method without munging the data would be to use COALESCE
Where ReceivedBy != PreviousReceivedBy
And Coalesce(ReceivedBy, PreviousReceivedBy) Is Not Null
NULL cannot equal anything, not even another NULL, so if any of the values are NULL, ReceivedBy != PreviousReceivedBy will evaluate as true.
Secondly, if both of the values are NULL, the Coalesce(ReceivedBy, PreviousReceivedBy) Is Not Null will evaluate as false, forcing those to be filtered.
If neither are NULL, the first condition would fail if they are equal.
Admittedly, it’s not saving too much code, but it is an improvement.
This can be easily grouped in parenthesis and copy/pasta’d for all remaining fields you need to check.
Where (ReceivedBy != PreviousReceivedBy And Coalesce(ReceivedBy, PreviousReceivedBy) Is Not Null)
And[Or] (Foo != Bar And Coalesce(Foo, Bar) Is Not Null)
...
WHERE ISNULL(ReceivedBy, -1) != ISNULL(PreviousReceivedBy, -1)
assuming the columns never have negative values
if either value is null then this returns false
It actually returns "unknown" rather than "false" (SQL uses three valued logic). But in a WHERE clause the combined predicates must evaluate to "true" for the row to be returned so the effect here is much the same.
From SQL Server 2022 you can use
WHERE ReceivedBy IS DISTINCT FROM PreviousReceivedBy
(NULLIF(#a, #b) IS NOT NULL) OR (NULLIF(#b, #a) IS NOT NULL) means "#a != #b even if one of them or both are null.

REPLACE empty string

I discover some behavior I didn't know before. Why this line of code does not work?
SELECT REPLACE('','','0') ==> returns ''
I can't even have '' in where condition. It just doesn't work. I have this from imported Excel where in some cells are no values but I'm not able to remove them unless I used LEN('') = 0 function.
There is nothing to replace in an empty string. REPLACE replaces a sequence of characters in a string with another set of characters.
You could use NULLIF to treat it as NULL + COALESCE (or ISNULL):
declare #value varchar(10);
set #value = '';
SELECT COALESCE(NULLIF(#value,''), '0')
This returns '0'.
You can use CASE for this.
(CASE WHEN *YOURTHING* = '' THEN '0' ELSE *YOURTHING* END)
AS *YOURTHING*
It does work. There are two proper behaviors here - first one is to return back empty string (what it does already), second one is to return infinite string full of zeroes.
Solved! > Check multiple scenarios like '', remove spaces, Null, string/numeric result
SELECT CASE WHEN LTRIM(RTRIM(ISNULL(mob, 0))) = '' THEN '0' ELSE LTRIM(RTRIM(ISNULL(mob, 0))) END MobileNo
FROM table1 WHERE emp_no = '01111'

TSQL Comparing 2 uniqueidentifier values not working as expected

I'm trying to compare 2 uniqueidentifier values as shown in the query below. However, if one value is null and one value isn't, the result is 'same'?! I'm sure that both values are uniqueidentifiers, and have also tried casting both values to uniqueidentifier to make absolutely sure. The 2 values being compared are coming from different databases with different collations. Does the collation make any difference? Any ideas would be appreciated.
select [result] = case when
[target].StaffID <> [source].StaffID then 'different'
else 'same'
end
from
...
If I replace the <> with an = the query then thinks that 2 null values don't match.
EDIT:
I used:
declare #empty uniqueidentifier
set #empty = '00000000-0000-0000-0000-000000000000'
... isnull(somevalue, #emtpy) <> isnull(othervalue, #empty) ...
NULL is neither equal to something nor equal to nothing. Generally you'd check for null values by comparing with IS NULL. For example,
somefield IS NULL
You could look into using COALESCE for what you're trying to do -- just make sure you use the same data types (in this case UniqueIdentifier):
...
case
when coalesce(t.StaffID,'00000000-0000-0000-0000-000000000000') <>
coalesce(t2.StaffID,'00000000-0000-0000-0000-000000000000')
then 'different'
else 'same'
end
...
http://sqlfiddle.com/#!3/181e9d/1
null is more of an unknown, it's not really a value. Unfortunately SQL will tell you that null = null is false.
Basically you have to cast nulls to empty strings than you can compare. We use IFNULL(value, replacement) to do that...
http://msdn.microsoft.com/en-us/library/ms184325.aspx
Hope this helps.
select case when null = null then 'equal' else 'not equal' end
Above will be "not equal"
select case when ISNULL(null, '') = ISNULL(null, '') then 'equal' else 'not equal' end
This one will be "equal"
And finally your case...
select [result] = case when
ISNULL([target].StaffID, '') <> ISNULL([source].StaffID, '') then 'different'
else 'same'
end
from

DATALENGTH() or ISNULL() to retrieve fields that are not null and not empty

Quite simply, which of the following methods is better in a WHERE clause to retrieve records where the FIELD_NAME is NOT NULL and NOT Empty
WHERE DATALENGTH(FIELD_NAME) > 0
or
WHERE ISNULL(FIELD_NAME, '') <> ''
Update
I have been informed that the first method gives spurious results for some types of fields... Agree?
Firstly,
select *
from table
where column <> ''
will give exactly the same results as
select *
from table
where isnull(column, '') <> ''
because records where the condition is UNKNOWN rather than FALSE will still be filtered out. I would generally go with the first option.
DATALENGTH counts trailing spaces, which a comparison with '' does not. It is up to you whether you want ' ' to compare unequal to ''. If you do, you need DATALENGTH. If you don't, simply compare with ''.
Note that for TEXT/NTEXT types, comparisons are not supported, but DATALENGTH is.
ISNULL is the best approach instead of DATALENGTH.
I would use
WHERE ISNULL(FIELD_NAME, '') <> ''
One issue that might come up is that a record with a space in it would not be returned. Are you looking for records like that?
I'm not sure about unexpected results from DATALENGTH. I would use the ISNULL method so that SQL Server doesn't need to spend time calculating the length of the record being compared. I don't know the performance difference between the two, just a gut feeling.
if your "not empty" condition encompasses spaces then i would use the nullif
select case when nullif(' ', '') is null then 'y' else 'n' end
y
declare #d varchar(50)
set #d = null
select case when nullif(#d, '') is null then 'y' else 'n' end
y
I would use one of the following:
where coalesce(field_name, '') <> ''
or
where field_name <> '' or field_name is not null
or
where field_name <> ''
The first is standard SQL (coalesce() is standard, isnull() is not). The last is not the most obvious, but NULL will fail the comparison and it allows the use of indexes.
RTRIM(LTRIM(ISNULL(FIELD_NAME, ''))) <> '' will handle spaces and NULLS