Comparing a value to a NULL in t-SQL - sql

I was curious if it's legal in t-SQL to compare a NULL to a value?
For instance, if I have:
WITH ctx AS(SELECT 123 AS n0, NULL AS n1)
SELECT n0 FROM ctx
WHERE ctx.n1 < 130
the WHERE clause in that case is always evaluated as FALSE. Is it something I can rely on?

You can't compare NULL with any other value, it will result in 'UNKNOWN'.
From msdn source
A value of NULL indicates that the value is unknown. A value of NULL
is different from an empty or zero value. No two null values are
equal. Comparisons between two null values, or between a NULL and any
other value, return unknown because the value of each NULL is unknown.

All boolean operations in T-Sql with null value returns 'UNKNOWN', which is recognized as false in clauses. You can use ISNULL function when you want set some default value.
for example in your case:
WITH ctx AS(SELECT 123 AS n0, NULL AS n1)
SELECT n0 FROM ctx
WHERE isnull(ctx.n1,0) < 130

It depends on the value of ANSI_NULLS.
http://msdn.microsoft.com/en-us/library/ms191270%28v=sql.90%29.aspx
When SET ANSI_NULLS is ON, a comparison in which one or more of the
expressions is NULL does not yield either TRUE or FALSE; it yields
UNKNOWN.
Transact-SQL supports an extension that allows for the comparison
operators to return TRUE or FALSE when comparing against null values.
This option is activated by setting ANSI_NULLS OFF. When ANSI_NULLS is
OFF, comparisons such as ColumnA = NULL return TRUE when ColumnA
contains a null value and FALSE when ColumnA contains some value
besides NULL.

The WHERE clause in the following = is also FALSE. You need to be very careful with NULLs
WITH ctx AS
(
SELECT 123 AS n0, NULL AS n1
)
SELECT *
FROM ctx
WHERE ctx.n1 = NULL

I've always used the EXISTS keyword along with EXCEPT like so
SELECT 1
WHERE EXISTS ((SELECT 1) EXCEPT (SELECT NULL))

Related

SQL Coalesce not returning any rows

I am using Postgres and have the following SQL statement:
SELECT *
FROM "osmlocal-dsd-de".t_osm_vehicle_image t
WHERE t.vehicle_config_id = 3
and image_type_id = 2
Which returns one row:
id vehicle_config_id cosy_url image_type_id
113 3 SomeValue 2
When I run the following:
SELECT * from "osmlocal-dsd-de".t_osm_vehicle_image t
WHERE t.vehicle_config_id = 3
and image_type_id = 2
and coalesce(t.cosy_url, '') = ''
Zero rows are returned.
I think my understanding of coalesce is wrong, because I would have expected one row still to be returned, because the cosy_url is not null.
Any advise on what I am doing wrong would be appreciated.
Your understanding of coalesce is wrong
It returns the first argument that is not null. If all arguments are null, the COALESCE function will return null
In your case t.cosy_url is not null it is equally SomeValue and your condition doesn't work because SomeValue is not equal ''
You seem to be misunderstanding coalesce(). It returns the first value that is not null.
In your case, you have:
coalesce(t.cosy_url, '')
Because t.cosy_url has a value ('SomeValue'), this evaluates to that value. The value is not '' so the expression returns false and the entire where clause returns false.
If you want non-NULL values, then use:
t.cosy_url is not null

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.

Querying a null boolean field

When querying a boolean field that is null why does
Select * From MyTableName where [boolfieldX] <> 1
not return any rows with null in [boolfieldX]? 1 <> null I would have expected rows with null to be returned.
It is not possible to test for NULL values with comparison operators, such as =, <, or <>.
You have to use the IS NULL and IS NOT NULL operators instead, or you have to use functions like ISNULL() and COALESCE()
Select * From MyTableName where [boolfieldX] <> 1 OR [boolfieldX] IS NULL
OR
Select * From MyTableName where ISNULL([boolfieldX],0) <> 1
Read more about null comparison in Stackoverflow Documentation
Read more about ISNULL() and COALESCE() Functions in Stackoverflow Documentation
I believe that it's because null is an unknown value. You can't query against an unknown 'value'. In my opinion, referring to null as a 'value' is an oxymoron because it represents an unknown. Using the operators "Is Null" and "Not Is Null" in conjunction with whatever selection criteria will return the desired results, or translating it by converting a null an alternate value will work like this: IsNull([boolfield], 'some compatible value')
Hi try to use this query:
select * from mytablename where [boolFieldX] is null And [boolFieldX] <> 1

"!="/NOT perhaps not working properly in SQLite

I have a table with about a hundred rows. It has a column is_gallery that contains either 1, 0, or NULL. If I do...
SELECT * WHERE is_gallery != 1
or
SELECT * WHERE NOT (is_gallery = 1)
it excludes the rows where is_gallery is null. I can manage to get a proper response if I do
SELECT * WHERE (is_gallery = 0 OR is_gallery is null)
But shouldn't the "!=" or NOT work? Isn't there a way to just return the rows where is_gallery doesn't equal 1 without testing for every other possibility?
You can use the IS and IS NOT operators instead of = and !=. These treat NULL like a normal value.
SELECT * FROM yourTable WHERE is_gallery IS NOT 1
The best thing to use is coalesce as in:
SELECT *
WHERE coalesce(is_gallery,0) != 1;
what coalesce does, is replaces any null value in that column with the second parameter. In the example above, any nulls in the "is_gallery" column will be replaced with 0 before it is compared with 1. So will of course return true.
On NULL realize that a NULL value isn't equal to ANYTHING - not even NULL itself. It cannot be compared - so when "comparing", it always will return FALSE. On NULL, it has a special operator which is "IS NULL" or "IS NOT NULL"

NULL IN (1,2,NULL) returns false

Why does this SQL-statement return 0?
SELECT CASE WHEN NULL IN (9,1,NULL) THEN 1 ELSE 0 END
SQL is based on three-valued logic, where there are three truth values: TRUE, FALSE, and UNKNOWN. The special NULL value is a placeholder for "missing data" and if you compare something, anything, with missing data the result is unknown.
For example, is <missing data> equal to <missing data>? It's impossible to know, so the result is UNKNOWN.
In this particular case, you are trying to find out if <missing data> is in a given list: since the data is missing, it's impossible to know if it's in the list, and the query returns 0.
SELECT CASE WHEN NULL IN (9,1,NULL) THEN 1 ELSE 0 END
I don't know what RDBMS you are using since some of them has configuration on how NULL will be treated.
NULL IN (9, 1, NULL)
can be written as
(NULL = 9) OR (NULL = 1) OR (NULL = NULL)
and not of them were TRUE nor FALSE. They are all NULL. Since there are only two paths in the CASE statement, it falls under ELSE block.
It depends on how NULL is treated in the specific server.
For instance in SQL SERVER you may set ANSI_NULLS off and have it return 1:
SET ANSI_NULLS OFF
SELECT CASE WHEN NULL IN (9,1,NULL) THEN 1 ELSE 0 END
For further info you should read the remarks section of SET ANSI_NULLS
Because you can't compare null values using comparison operators. You can only use is null or is not null or using functions like COALESCE(), ISNULL(). For example
SELECT CASE WHEN COALESCE(NULL,-1) IN (9,1,-1) THEN 1 ELSE 0 END
Null mean not defined.
NUll object is not equal to other NULL
NULL==1 return false
NUll==2 return false
NULL==NULL return also false