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.
Related
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
I have a WHERE statement that looks like this:
WHERE
((#Value1 IS NULL AND [value1_id] IS NULL) OR [value1_id] = ISNULL(#Value1, [value1_id]))
AND
((#Value2 IS NULL AND [value2_id] IS NULL) OR [value2_id] = ISNULL(#Value2, [value2_id]))
AND
((#Value3 IS NULL AND [value3_id] IS NULL) OR [value3_id] = ISNULL(#Value3, [value3_id]))
AND
((#Value4 IS NULL AND [value4_id] IS NULL) OR [value4_id] = ISNULL(#Value4, [value4_id]))
AND
((#Value5 IS NULL AND [value5_id] IS NULL) OR [value5_id] = ISNULL(#Value5, [value5_id]))
AND
((#Value6 IS NULL AND [value6_id] IS NULL) OR [value6_id] = ISNULL(#Value5, [value6_id]))
I need to add some conditional logic inside of the WHERE so I can do special things with Value5 and Value6. Basically, if Value5, Value6, or another value is null, I want to use the Value 5 and Value 6 lines as is. If all three values aren't NULL, I need to run some calculations on the values.
Any ideas on what the best action would be?
Use CASE
"CASE can be used in any statement or clause that allows a valid expression. For example, you can use CASE in statements such as SELECT, UPDATE, DELETE and SET, and in clauses such as select_list, IN, WHERE, ORDER BY, and HAVING."
http://msdn.microsoft.com/en-us/library/ms181765.aspx
You can perform calculations inside of a SELECT like this:
SELECT
CASE
WHEN #Value7 is not null THEN #Value7 * 100
ELSE Value7 * 100
END
FROM #T
WHERE (#Value1 is null and Value1 is null)
AND (#Value2 is null and Value2 is null)
It's still not very clear what exactly you're trying to accomplish. Is the WHERE not inside a SELECT? Provide a clear, concise example if the above is not correct.
Basically, if Value5, Value6, or another value is null, I want to use
the Value 5 and Value 6 lines as is. If all three values aren't NULL,
I need to run some calculations on the values.
I think this will be hard to do with for example a CASE inside the WHERE clause. You might be better off with an IF construction outside of your SELECT statements:
IF (#Value5 IS NULL OR #Value6 IS NULL OR #OtherValue IS NULL)
BEGIN
<Statement1AsIs>
END
ELSE
BEGIN
<Statement2WithComputations>
END
Rewrite WHERE clause
On a short sidenote, I know you did not ask for it, but I have the feeling there must be a 'clearer' way to write these
((#Value1 IS NULL AND [value1_id] IS NULL) OR [value1_id] = ISNULL(#Value1, [value1_id]))
I've been playing around a bit and this is one way to say the same:
COALESCE(#Value1, [value1_id], 1001) = COALESCE([value1_id], 1001)
This is another one, equivalent and shorter:
#Value1 IS NULL OR [value1_id] = ISNULL(#Value1, [value1_id])
Check out this SQL Fiddle to see the equivalence.
I am working on a select statement in SQL and am running into issues trying to create a where clause that includes a case statement or an if else statement. I want to select records based on the value of a variable. If the variable is 'True' then only return records from the select statement where a column is null. If the variable is not 'True' then return all records regardless if that columns is null.
Any tips on how to do this?
Below is a simple example of what i am trying to do:
declare #option1 as varchar(5)
--This can be True or False so to test i just put the Set option below
set #option1 = 'True'
Select a,b,c,d...
from ...
where d = case when #option1 = 'True' then NULL End
This is the part where i do not know what to do. I only need to filter out the records if the variable is 'True' so not sure what to put in the else section of the case.
You can't test for d = NULL as your CASE statement does because that will always return false since NULL is not equal to NULL (unless you set ANSI_NULLS to 'off').
The simplest thing to do would be to change the WHERE clause to this:
WHERE #option1 = 'False' OR d IS NULL
If you prefer to use a CASE statement for some reason, you can write it like this:
WHERE 1 = CASE WHEN #option1 = 'False' THEN 1
WHEN #option1 = 'True' AND d IS NULL THEN 1
ELSE 0
END
This:
UPDATE: PinnyM has straightened me out on this. I am leaving my embarrassing logically flawed argument here for the education of the masses. The solution I propose below after "Try this" is certainly still valid, but PinnyM's solutions is by far more elegant and should be used.
WHERE #option1 = 'False' OR d IS NULL
Will always return all the results given his current select statement (assuming #Option1 is simply a flag parameter passed in).
Try this:
SELECT a, b, c, d
WHERE
-- Returns only rows where d is null (if #Option1 is True)
(#Option1 = 'True' AND d IS NULL)
OR
-- returns all the rows (if #Option1 is False)
(#Option1 = 'False')
I understand why these 2 statements are false
NULL LIKE 'X'
NULL NOT LIKE 'X'
However, what I don't understand is why these are :
NOT (NULL LIKE 'X')
NOT (NULL NOT LIKE 'X')
For example, these two statements should, I think, return different values :
SELECT CASE WHEN NOT (NULL LIKE 'X') THEN 'True' ELSE 'False' END
SELECT CASE WHEN (NULL LIKE 'X') THEN 'True' ELSE 'False' END
SQL uses a three-valued logic. You say that these are all false:
NULL LIKE 'X'
NULL NOT LIKE 'X'
NOT (NULL LIKE 'X')
NOT (NULL NOT LIKE 'X')
but that's actually not true. They're all null, which is neither true nor false.
A WHEN or WHERE clause rejects non-true values, which means null values as well as false ones, so it may seem like null is the same as false, but as you've noticed, it's not. :-)
NULL is always NULL you cannot compare it to something like that. It cannot be true or false which is what you are looking for as an answer.
It is just like you cannot compare, this will always return zero rows because NULL is an unknown value.
SELECT *
FROM yourTable
WHERE yourCol = NULL
NULL is always undefined.
So the value of
not (NULL like X) is undefined
and also
not (NULL not like X) is undefined
The best approximation of Undefined is NULL so both statements evaluate to NULL (NOT, you understand, equal NULL, we don't know what they equal)
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))