Comparing 2 null datetimes not returning equal - sql

In an update trigger I am comparing 2 nullable datetimes, that both happen to be null in my current test and it is returning that they are not equal.
#ExpirationDateChanged = case when i.ExpirationDate = d.ExpirationDate then 0 else 1 end
In this case i.ExpirationDate and d.Expiration date are null but instead of getting the expected 0, I am getting 1.
Can anyone explain this behavior?

NULL is not equal to NULL using equality comparison.
Comparing NULL values for equality is UNKNOWN under ANSI setting (the default)
You need to add an extra condition to explicitly test both columns for NULL using IS NULL:
#ExpirationDateChanged = case
when (i.ExpirationDate IS NULL AND d.ExpirationDate IS NULL) OR
i.ExpirationDate = d.ExpirationDate then 0
else 1
end

Related

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.

Is null > 0 (integer) [duplicate]

This question already has answers here:
What is NULL in SQL?
(5 answers)
Closed 8 years ago.
If I have in my sql something like that:
SELECT *
FROM SOMETABLE
WHERE ((SELECT ONECOLUMN FROM ANOTHERTABLE WHERE ID = 42) > 0)
If I got a NULL in ONECOLUMN it will be greater then 0?
As NULL means "not known", NULL is not greater than 0. It's not smaller either. It's not known. Hence NULL > 0 results in NULL, rather than in TRUE or FALSE.
NULL is not greater than zero.
NULL is not equal to zero.
NULL is not less than zero.
Any given integer you might choose would meet exactly one of these three conditions. But not NULL.
NULL isn't an integer. It's a marker indicating that a value is not present. This could mean that a value exists, but is unknown. It could also indicate that no value exists in this context.
You can use the ISNULL function to find out whether a NULL is present instead of a value. But if you compare a value with zero, and there is a NULL in place of the value, you won't get either TRUE or FALSE as a result.
If you are confused, you're in good company.
How about checking for NULL and then returning 0 if true otherwise return the value of ONECOLUMN:
SELECT *
FROM SOMETABLE
WHERE (
SELECT CASE WHEN ISNULL(ONECOLUMN) THEN 0 ELSE ONECOLUMN END
FROM ANOTHERTABLE
WHERE ID = 42
) > 0
Null is unknown and it cannot be compared to any value. It cannot be greater or lesser.
SELECT *
FROM SOMETABLE
WHERE (
IsNull((SELECT ONECOLUMN
FROM ANOTHERTABLE
WHERE ID = 42), IntValue) > 0
)
If you need to accept the Null so IntValue can be 1 otherwise -1 is OK.

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

Comparing a value to a NULL in t-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))

How does 'in' clause works in oracle

select 'true' from dual where 1 not in (null,1);
when we execute this which will result nothing
what my question is:
is the above query is logically equivalent to
select 'true' from dual where 1 != null and 1 != 1;
which will result nothing just as above statement
Please clarify?
Correct (but note that IN is an operator, not a clause and it works like this in SQL in general, not only for Oracle).
where 1 not in (null,1)
is equivalent to:
where 1 != null and 1 != 1
which should really be written as:
WHERE 1 NOT IN (NULL, 1)
and
WHERE 1 <> NULL AND 1 <> 1
which is the same as:
WHERE (1 <> NULL) AND (1 <> 1)
which evaluates to:
WHERE UNKNOWN AND FALSE
and further as:
WHERE FALSE
So, it correctly returns no rows.
Notice that if you had WHERE 1 NOT IN (NULL, 2), it would evaluate to WHERE UNKNOWN (left as an exercise) and no rows would be returned either.
The issue of your script in comparing with NULL value. You should use
column is null and column = 1
Actually NULL is an undefined value. Any comparation with NULL gives neither True nor False but NULL. Even NULL = NULL
That's why your 1 not in (null,1) doesn't work.
Yes they are.
select something from table where column not in (1,2,3);
is equivalent to
select something from table where column != 1 and column != 2 and column != 3;
The IN statement is a collection of OR statements, while NOT IN is a collection of AND statements - but it is also not equal to.
So the NOT IN is equivalent to:
1 <> NULL
AND 1 <> 1
AND ...
While the IN would be equivalent to:
1 = NULL
OR 1 = 1
OR ...
Note that having NULL in the collection will not work, due to the quirky nature of NULL.
Yes. It is correct. Also NULL values should be compared with IS NULL