Confusing null compares combined with NOT - sql-server

I realize that comparing NULL to any other value (including NULL) will always result in false.
DECLARE #IsSet bit = NULL;
SELECT IIF(#IsSet = 1, 'true', 'false')
SELECT IIF(#IsSet != 1, 'true', 'false')
This outputs:
false
false
But this is part that confuses me:
SELECT IIF(NOT(#IsSet = 1), 'true', 'false')
SELECT IIF(NOT(#IsSet != 1), 'true', 'false')
This also outputs:
false
false
I would expect that the NOT would have flipped the value to TRUE. (Which it does if #IsSet is set to 0 for the first expression)
It seems that the compare to the null value has some power over the boolean logic outside the parenthesis.
But the null compare is not all powerful over boolean logic:
SELECT IIF((#IsSet = 1) OR (1=1), 'true', 'false')
SELECT IIF((#IsSet != 1) OR (1=1), 'true', 'false')
This returns:
true
true
I don't understand what is happening here, but I assume that this is done on purpose. But I don't know why.
Can someone explain why NOT(NULL!=1) does not equal true.

A comparison with NULL results in UNKNOWN rather than TRUE or FALSE. NOT UNKNOWN also results in UNKNOWN, which is neither TRUE nor FALSE. One cannot "flip" UNKNOWN to a Boolean value using NOT.
This 3-way logic requires one to use IS NULL or IS NOT NULL to test for NULL values rather than traditional Boolean logic.

The way you are using NOT is not correct. You'll get error, If you only execute the NOT condition as follows:
SELECT NOT(#IsSet = 1)
When you enclose your incorrect usage of NOT condition inside IIF condition, SQL server won't show you the error, However the statement will be evaluated to false output.
If you want to explicitly check for NULL value, then following practice can be adopted.
SELECT IIF(#IsSet IS NULL, 'true', 'false')
Lastly, the following condition is returning 'true' in output, because one of the OR condition (1==1) is always evaluating to 'true', hence the overall output of the IIF statement is true.
SELECT IIF((#IsSet = 1) OR (1=1), 'true', 'false')
SELECT IIF((#IsSet != 1) OR (1=1), 'true', 'false')

Related

how to use array in any() function?

I can run a statement like
select 'a' like any('a',b'), but is it still possible to run this statement if ('a','b') were in an array?
select 'a' like any(array_construct('a','b')) doesn't work.
You can use a JavaScript UDF to approximate this behavior. Rather than trying to get JavaScript to simulate the like syntax, it's probably better to use richer, more expressive Regular Expressions. This UDF will return true if any array member matches that regexp pattern, false if none do, and null if the regex pattern is invalid or it encounters an unexpected error:
create or replace function LIKE_ANY_ARRAY("arr" array, "pattern" string)
returns boolean
language javascript
strict immutable
as
$$
"option strict"
try {
var regex = RegExp(pattern,"g")
for(let i=0; i<arr.length; i++) {
if(regex.test(arr[i])) return true;
}
return false;
} catch {
return null;
}
$$;
select like_any_array(array_construct('a','b'), 'a'); -- True
select like_any_array(array_construct('abcde','b'), '[a-z]{5}'); -- True
select like_any_array(array_construct('abcde','b'), '[a-z]{8}'); -- False, none have enough letters
select like_any_array(array_construct(2,'b'), '[a-z]{8}'); -- Auto-casting
select like_any_array(array_construct('a', 'b'), '['); -- Invalid regex pattern returns NULL
This is returning a Boolean to approximate the behavior of the LIKE ANY function, but could very easily be converted to returning an array of all the matching members or an integer representing the first match, etc.
You could turn you array into a string that resembles a regex and match against that
where 'a' rlike array_to_string( ['a','b'],'|')

SQL ISNULL CASE

Can someone please explain to me in laymen terms why do we put ISNULL? I have an understanding of IS NULL but can't seem to put it together in this CASE context and what would be the impact if you didn't put it.
UpdateTime refers to when a particular row has been updated with new information
Example 1:
CASE WHEN ISNULL(DB1.UpdateTime,'') >= ISNULL(DB2.UpdateTime,'') THEN ISNULL(DB1.UpdateTime,'')
ELSE DB2.UpdateTime
END AS UpdateTime
FROM dbo.DB1_Result DB1 INNER JOIN dbo.DB2 DB2
ON DB1.ID = DB2.ID
Example 2:
StartTime = 01/01/2022
WHERE
(ISNULL(DB1.UpdateTime,'') >= #StartTime
Thank you!
SQL uses trinary logic: true, false, and null. null means the value is unknown, it could be anything.
If you try to compare anything with null you get null, even null. null = anything is null. null <> anything is null. null < anything is null. null > anything is also null.
Null is neither equal nor not equal to itself. null = null is null and null <> null is null. This is why we write x is null not x = null.
If you were to write
case
when DB1.UpdateTime >= DB2.UpdateTime then
db1.UpdateTime
else
db2.UpdateTime
end
and if either one is null the DB1.UpdateTime >= DB2.UpdateTime will be null. Case will treat that as false and return DB2.UpdateTime. If either value is null, you always get DB2.UpdateTime even if it's null and DB1.UpdateTime is not.
They don't want that. If DB2.UpdateTime is null, they want to return DB1.UpdateTime. If DB1.UpdateTime is null, they want to return DB2.UpdateTime. So they've used isnull to convert null to something that will compare as less than any time; '' works for that.
Demonstration.
Note: ELSE DB2.UpdateTime may want to be ELSE isnull(DB2.UpdateTime, '') to be consistent with THEN ISNULL(DB1.UpdateTime,'').
Note: isnull is a SQL Server extension. The SQL standard is coalesce.
Note: '' is not a timestamp. It happens to work, but other SQL servers will reject it. 1900-01-01 would be better.

Column boolan to value GUI

I cant seem to find a answer to this. I am using GUI to create a table and a column in there is called DuesPaid. I need this column boolan value to be false and it has the datatype of bit. How might one do this am I just missing where its at?
BIT is the type to store boolean values. 1 will mean true, and 0 will mean false. You can also compare it to strings 'true' and 'false'.
If you run this query:
SELECT IIF(CAST(1 AS BIT) = 'true', 'Yes', 'No') as [1 = true?]
, IIF(CAST(0 AS BIT) = 'false', 'Yes', 'No') as [0 = false?]
, IIF(CAST(0 AS BIT) = 'true', 'Yes', 'No') as [0 = true?]
You'll get the following:
1 = true? 0 = false? 0 = true?
--------- ---------- ---------
Yes Yes No

Simplifying a SQL Server query with a shortcut

I have a query where many columns could be blank or null. They actually have longer names than the example below which I am using as an example:
select *
from table1
where field1 is not null and field1 != '' and
field2 is not null and field2 != ''
...etc
It gets tiresome having to type
x is not null and x != ''.
Is there some way to specify "x is not null and x != ''"?
Like for Java with
StringUtils.isNotEmpty(x)
I use
where isnull(x, '') <> ''
a lot. I find it a bit easier to "understand" than nullif.
-- EDIT ---------------------------------------
I missed that they were all ANDed together. So, if all N fields must be non-null and not empty, assuming that all fields are strings (varchars), this should do it:
where isnull(field1 + field2 + field3 + ... + fieldN, '') <> ''
First, the strings are concatenated together:
If any are null, the result will be null
If none are null and all are empty, the result will be an empty string
Else, the result will be a non-empty string
Next, the results are isnulled:
If the concatenated value is null, it is set to an empty string
Else, you get the concatenated contents (empty or not-empty string)
Last, compare that with the empty string:
If True, then either all are empty or one or more is null
If False, none are null and at least one is not empty
Try
WHERE NULLIF(field1, '') IS NULL
For SQL Server, I would use COALESCE for this:
WHERE COALESCE(field1, '') > ''
ISNULL also works
If you want to exclude rows where every field is null or blank you can do it like this:
WHERE COAlESCE(Field1,Field2,Field3,Field4,Field5,'') <> ''

Comparison operations on xs:boolean in SQL Server 2005

Can anyone explain to me the results of this query:
declare #xml xml;
set #xml = '<node attribute="true">Val</node>';
select
T.c.query('xs:boolean(#attribute[1])') Value,
T.c.query('xs:boolean(#attribute[1]) = false') ValueEqualsFalse,
T.c.query('xs:boolean(#attribute[1]) = true') ValueEqualsTrue,
T.c.query('xs:boolean(#attribute[1]) != false') ValueNotEqualsFalse,
T.c.query('xs:boolean(#attribute[1]) != true') ValueNotEqualsTrue
from #Xml.nodes('node') T(c);
The first column, Value, returns true. The rest all return false. So having managed to cast the value to the correct type, how do I actually check it's value?
you need to use fn:false() or false() and fn:true() or true() instead of just writing true or false.
This is a correct example:
T.c.query('xs:boolean(#attribute[1]) = false()') ValueEqualsFalse,
T.c.query('xs:boolean(#attribute[1]) = true()') ValueEqualsTrue,
T.c.query('xs:boolean(#attribute[1]) != false()') ValueNotEqualsFalse,
T.c.query('xs:boolean(#attribute[1]) != true()') ValueNotEqualsTrue
If you don't use the function false(), false is processed as a path expression, i.e. the processor thinks you are querying for a <false /> element. Therefore, there only exists a function for the constants true and false, because this is the only way to distinguish between the boolean constants and a path expression.
More in detail, using the negation would still return false in each example.
This is not what you want (just to demonstrate):
T.c.query('not(xs:boolean(#attribute[1])) = false') ValueEqualsFalse,
T.c.query('not(xs:boolean(#attribute[1])) = true') ValueEqualsTrue,
T.c.query('not(xs:boolean(#attribute[1])) != false') ValueNotEqualsFalse,
T.c.query('not(xs:boolean(#attribute[1])) != true') ValueNotEqualsTrue
the literals false and true both are evaluated to an empty sequence which neither matches the boolean value false() nor the boolean value true().

Resources