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().
Related
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'],'|')
The stored procedure I am working on has 2 parameters #IsApproved (BIT) and CustomId (INT).
Now in my WHERE clause I want to check if #IsApproved or not, if it is approved then use A.[FunctionalId] = #CustomId else A.[InstallId] = #CustomId.
I tried like this, but it throws an error.
SELECT
AppKeyId, AppName, AppVersion
FROM
dbo.[Application] A
WHERE
CASE
WHEN #IsApproved = 1 THEN A.[FunctionalId] = #CustomId
ELSE A.[InstallId] = #CustomId
END
A CASE expression returns a scalar value, not a boolean result; THEN A.[FunctionalId] = #CustomId isn't valid as it's not a scalar value.
Use an OR:
SELECT AppKeyId,
AppName,
AppVersion
FROM dbo.Application A
WHERE (#IsApproved = 1 AND A.[FunctionalId] = #CustomId)
OR (#IsApproved = 0 AND A.[InstallId] = #CustomId);
I am looking for a partial match / using some kind of a wild card to do string matching in SNOWFLAKE arrays.
SELECT ARRAY_CONTAINS('HELLO'::VARIANT, ARRAY_CONSTRUCT('HELLO', 'HI'))
<TRUE>
Tried like this , but no luck
SELECT ARRAY_CONTAINS('HELL%'::VARIANT, ARRAY_CONSTRUCT('HELLO', 'HI'))
<FALSE>
Is there any other way to do this ?
Maybe you can convert the array (temporarily) to string and use CONTAINS operator?
SELECT CONTAINS( ARRAY_TO_STRING( ARRAY_CONSTRUCT('WHY','HELLO', 'HI'),',') , 'HELL' );
SELECT ARRAY_CONSTRUCT('HELLO', 'HI') AS aa
,':' || ARRAY_TO_STRING(aa,';') || ':' AS bb
,bb like '%:HELL%'
so this is a dirty solution, but it would "work"
You can do this with a JavaScript UDF. This example uses RegExp instead of the like function wildcards. It's overloaded so you can use standard RegExp or one with parameters (i for case insensitivity in particular). This returns a boolean to approximate the behavior of ARRAY_CONTAINS. If you want, you can do a simple modification to return the value of "i", which will be the ordinal position of the first match in the array, or -1 if it leaves the loop (not found).
create or replace function ARRAY_CONTAINS_REGEXP(REGEXP_EXPRESSION string, A array)
returns boolean
language javascript
as
$$
var rx = new RegExp(REGEXP_EXPRESSION);
for (var i = 0; i < A.length; i++){
if (A[i].search(rx) != -1) return true;
}
return false;
$$;
create or replace function ARRAY_CONTAINS_REGEXP(REGEXP_EXPRESSION string, A array, REGEX_PARAMS string)
returns boolean
language javascript
as
$$
var rx = new RegExp(REGEXP_EXPRESSION, REGEX_PARAMS);
for (var i = 0; i < A.length; i++){
if (A[i].search(rx) != -1) return true;
}
return false;
$$;
SELECT ARRAY_CONTAINS_REGEXP('HELL.'::VARIANT, ARRAY_CONSTRUCT('HELLO', 'HI')); -- True. Wildcard match.
SELECT ARRAY_CONTAINS_REGEXP('HELL'::VARIANT, ARRAY_CONSTRUCT('HELLO', 'HI')); -- True. Partial match.
SELECT ARRAY_CONTAINS_REGEXP('\\bHEL\\b'::VARIANT, ARRAY_CONSTRUCT('HELLO', 'HI')); -- False. Full word matching.
SELECT ARRAY_CONTAINS_REGEXP('hel'::VARIANT, ARRAY_CONSTRUCT('HELLO', 'HI')); -- False. Case sensitive.
SELECT ARRAY_CONTAINS_REGEXP('HeL'::VARIANT, ARRAY_CONSTRUCT('HELLO', 'HI'), 'i'); -- True. Case insensitive partial match.
SELECT ARRAY_CONTAINS_REGEXP('\\bHeL\\b'::VARIANT, ARRAY_CONSTRUCT('HELLO', 'HI'), 'i'); -- False. Full word insensitive match.
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')
I have a stored procedure that is relying on an IF #awardEmployees IS NOT NULL statement.
I have the variable defined at the top:
#awardEmployees XML = NULL
and then before the statement I am trying to set this:
SET #awardEmployees = NULLIF(#awardEmployees, '')
Which is returning the error:
The data types xml and varchar are incompatible in the equal to operator.
My code sends either an XML string or an empty string based on if anything was entered in the search field so I am trying to set it to NULL if it was empty.
Any other ways to accomplish this?
Use the exist exist() method - it will return 0, representing False, if it returns an empty result:
declare #awardEmployees XML = '<employees><employee><empID>1234</empID></employee></employees>'
IF #awardEmployees.exist('/employees/employee') > 0
BEGIN
print '#awardEmployees IS NOT NULL'
END
ELSE
BEGIN
print '#awardEmployees IS NULL'
END
declare #awardEmployees2 XML = ''
IF #awardEmployees2.exist('/employees/employee') > 0
BEGIN
print '#awardEmployees2 IS NOT NULL'
END
ELSE
BEGIN
print '#awardEmployees2 IS NULL'
END
See here.