Why does COALESCE fail when I add a MAX to the argument? - snowflake-cloud-data-platform

I'll make this as simple as I can (tall order for me, according to every presentation course I've taken).
SELECT '2021-03-23 14:30:56.541 +0000'::TIMESTAMP; -- Works
SELECT MAX('2021-03-23 14:30:56.541 +0000'::TIMESTAMP); -- Works (obv.)
SELECT COALESCE('2021-03-23 14:30:56.541 +0000'::TIMESTAMP, ''); -- Works
SELECT COALESCE(MAX('2021-03-23 14:30:56.541 +0000'::TIMESTAMP), ''); -- Timestamp '' is not recognized
Curiously, if I run DESCRIBE RESULT LAST_QUERY_ID(); on the working ones, the type of the column gets reported as TIMESTAMP_TZ(9). So it's doesn't seem (to me at least) like a type issue. Interestingly, if you manually cast the "bad" one to TEXT, it works.
SELECT COALESCE(MAX('2021-03-23 14:30:56.541 +0000'::TIMESTAMP)::TEXT, ''); -- Works

Because the second value in the COALESCE is TEXT and the first is TIMESTAMP and they do not implicitly auto convert.
SELECT
MAX('2021-03-23 14:30:56.541 +0000'::TIMESTAMP) as a
,SYSTEM$TYPEOF(a) as type_a
,'' as b
,SYSTEM$TYPEOF(b) as type_b
--,COALESCE(a, b)
;
A
TYPE_A
B
TYPE_B
2021-03-23 14:30:56.541
TIMESTAMP_NTZ(9)[SB16]
VARCHAR(1)[LOB]
Now if you use a TRY_TO_TIMESTAMP if will not explode on the failed covert of an empty string into TIMESTAMP, but will give you NULL, which COALESCE will accept as the types are now the same, but in reality, that is as useless as COALESCE is trying to replace NULL..
SELECT
MAX('2021-03-23 14:30:56.541 +0000'::TIMESTAMP) as a
,SYSTEM$TYPEOF(a) as type_a
,'' as b
,SYSTEM$TYPEOF(b) as type_b
,try_to_timestamp(b) as try_b
,SYSTEM$TYPEOF(try_b) as type_try_b
,COALESCE(a, try_b) as col_res
;
A
TYPE_A
B
TYPE_B
TRY_B
TYPE_TRY_B
COL_RES
2021-03-23 14:30:56.541
TIMESTAMP_NTZ(9)[SB16]
VARCHAR(1)[LOB]
TIMESTAMP_NTZ(9)[SB16]
2021-03-23 14:30:56.541
So then the question becomes, what are you really trying to get as the output by COALESCE'ing the first value?
"Your 'It works when I make the timestamp a string'" is because now both are strings, so their types match. Which if you are wanting to avoid null results, and want a empty string, then converting to string makes sense as a thing to do.

Related

Is there a way to find values that contain only 0's and a symbol of any length?

I want to find strings of any length that contain only 0's and a symbol such as a / a . or a -
Examples include 0__0 and 000/00/00000 and .00000
Considering this sample data:
CREATE TABLE dbo.things(thing varchar(255));
INSERT dbo.things(thing) VALUES
('0__0'),('000/00/00000'),('00000'),('0123456');
Try the following, which locates the first position of any character that is NOT a 0, a decimal, a forward slash, or an underscore. PATINDEX returns 0 if the pattern is not found.
SELECT thing FROM dbo.things
WHERE PATINDEX('%[^0^.^/^_]%', thing) = 0;
Results:
thing
0__0
000/00/00000
00000
The opposite:
SELECT thing FROM dbo.things
WHERE PATINDEX('%[^0^.^/^_]%', thing) > 0;
Results:
thing
0123food456
Example db<>fiddle
I can see a way of doing this... But it's something that wouldn't perform well, if you think about using it as a search criteria.
We are going to use a translate function on SQL Server, to replace the allowed characters, or symbols as you've said, with a zero. And then, eliminates the zeroes. If the result is an empty string, then there are two cases, or it only had zeroes and allowed characters, or it already was an empty string.
So, checking for this and for non-empty strings, we can define if it matches your criteria.
-- Test scenario
create table #example (something varchar(200) )
insert into #example(something) values
--Example cases from Stack Overflow
('0__0'),('000/00/00000'),('.00000'),
-- With something not allowed (don't know, just put a number)
('1230__0'),('000/04560/00000'),('.00000789'),
-- Just not allowed characters, zero, blank, and NULL
('1234567489'),('0'), (''),(null)
-- Shows the data, with a column to check if it matches your criteria
select *
from #example e
cross apply (
select case when
-- If it *must* have at least a zero
e.something like '%0%' and
-- Eliminates zeroes
replace(
-- Replaces the allowed characters with zero
translate(
e.something
,'_./'
,'000'
)
,'0'
,''
) = ''
then cast(1 as bit)
else cast(0 as bit)
end as doesItMatch
) as criteria(doesItMatch)
I really discourage you from using this as a search criteria.
-- Queries the table over this criteria.
-- This is going to compute over your entire table, so it can get very CPU intensive
select *
from #example e
where
-- If it *must* have at least a zero
e.something like '%0%' and
-- Eliminates zeroes
replace(
-- Replaces the allowed characters with zero
translate(
e.something
,'_./'
,'000'
)
,'0'
,''
) = ''
If you must use this as a search criteria, and this will be a common filter on your application, I suggest you create a new bit column, to flag if it matches this, and index it. Thus, the increase in computational effort would be spread on the inserts/updates/deletes, and the search queries won't overloading the database.
The code can be seen executing here, on DB Fiddle.
What I got from the question is that the strings must contain both 0 and any combination of the special characters in the string.
If you have SQL Server 2017 and above, you can use translate() to replace multiple characters with a space and compare this with the empty string. Also you can use LIKE to enforce that both a 0 and any combination of the special character(s) appear at least once:
DECLARE #temp TABLE (val varchar(100))
INSERT INTO #temp VALUES
('0__0'), ('000/00/00000'), ('.00000'), ('w0hee/'), ('./')
SELECT *
FROM #temp
WHERE val LIKE '%0%' --must have at least one zero somewhere
AND val LIKE '%[_/.]%' --must have at least one special character(s) somewhere
AND TRANSLATE(val, '0./_', ' ') = '' --translated zeros and sp characters to spaces equivalent to an empty string
Creates output:
val
0__0
000/00/00000
.00000

Leetcode SQL 1440. Evaluate Boolean Expression

Table Variables:
Column Name
Type
name
varchar
value
int
name is the primary key for this table.
This table contains the stored variables and their values.
Table Expressions:
Column Name
Type
left_operand
varchar
operator
enum
right_operand
varchar
(left_operand, operator, right_operand) is the primary key for this table.
This table contains a boolean expression that should be evaluated.
operator is an enum that takes one of the values ('<', '>', '=')
The values of left_operand and right_operand are guaranteed to be in the Variables table.
Write an SQL query to evaluate the boolean expressions in Expressions table.
Return the result table in any order.
I am working on a SQL problem as shown in the above. I used MS SQL server and tried
SELECT
left_operand, operator, right_operand,
IIF(
(left_values > right_values AND operator = '>') OR
(left_values < right_values AND operator = '<' ) OR
(left_values = right_values AND operator = '='), 'true', 'false') as 'value'
FROM
(SELECT *,
IIF(left_operand = 'x', (SELECT value FROM Variables WHERE name='x')
, (SELECT value FROM Variables WHERE name='y')) as left_values,
IIF(right_operand = 'x', (SELECT value FROM Variables WHERE name='x')
, (SELECT value FROM Variables WHERE name='y')) as right_values
FROM Expressions) temp;
It works well on the test set but gets wrong when I submit it.
I think my logic is correct, could anyone help take a look at it and let me know what my problem is?
Thank you!
It feels like your example code is a lot more complicated than it needs to be. That's probably why it's failing the check. In your FROM you're using sub-selects but really a simple inner join would work much simpler. Also, if there were variables other than X and Y it doesn't look like your example code would work. Here's my code that I wrote in Postgres (should work in any SQL though).
SELECT e.left_operand, l.value as left_val, e.operator, e.right_operand, r.value as right_val,
CASE e.operator
WHEN '<' THEN
(l.value < r.value)
WHEN '=' THEN
(l.value = r.value)
WHEN '>' THEN
(l.value = r.value)
END as eval
FROM
expression as e
JOIN
variable as l on e.left_operand = l.name
JOIN
variable as r on e.right_operand = r.name
Here's a screenshot of my output:
I also have a db-fiddle link for you to check out.
https://www.db-fiddle.com/f/fdnJVSUQHS9Vep4uDSe5ZP/0

'<number>D' causes CAST to error

I'm finding this to be truly bizarre behaviour.
Here is my T-SQL:
declare #testText nvarchar(1000);
set #testText = '17D4,A,1';
select txt_value from fn_ParseText2Table (#testText , ',' )
where fn_ParseText2Table is a function that parses the text into a table where you can get the txt, int, and floating point values, if they work.
The 17D4 is a product code that I'm trying to extract within a larger query, and all other 3817 records work fine.
select (
select txt_value
from fn_ParseText2Table(t.primaryKeyValues , ',' ) as pk
where position = 1) as product_NBR
from database.dbo.tablesToParse as t
where t.tableName = 'ProductData'
I found the function here.
What I've found is that if the string starts with some numbers (I've tested anywhere from 1-4 ) followed by 'D', it fails with the 'Error converting data type varchar to numeric.' message.
All other combinations of text work. Lower case d is fine. C is fine, E, F, etc. So '17F5,A,1' is fine. Also 'asdf 17D5,A,1' is fine. '1D,A,1' is not fine.
I'm very confused. Is there a special escape character in T-SQL for 'D'?
Update:
I should clarify that the error occurs inside fn_ParseText2Table()
Update 2
It's SQL server 10 - 64 bit, running on a windows 2008 server.
As well, I've tested this in a sql mgr query window:
declare #testText nvarchar(1000);
set #testText = '17D4';
select isnumeric( #testText )
The IsNumeric() call returns 1, which is why the fn_ParseText2Table() function tries to cast it to an in and it fails. I could add an extra check to that function to lower the text first and see if that's also numeric.
I figured it was related to floating-point literals but I was surprised it wouldn't cast to a numeric type. I guess it only works when float is the target type. You'll find the explanation here:
http://www.sqlservercentral.com/Forums/Topic202581-8-1.aspx
This behavior doesn't match up with other SQL Server literals, ie, constants:
select 1d -- literal 1 with d treated as the column alias
select 1e -- literal 1.0 as a float
select cast('1d' as float), cast('1e' as float) -- neither of these will cast without an exponent value
select cast('1d0' as float), cast('1e0' as float) -- these work though

Find all special characters in a column in SQL Server 2008

I need to find the occurrence of all special characters in a column in SQL Server 2008. So, I don't care about A, B, C ... 8, 9, 0, but I do care about !, #, &,, etc.
The easiest way to do so, in my mind, would exclude A, B, C, ... 8, 9, 0, but if I wrote a statement to exclude those, I would miss entries that had ! and A. So, it seems to me that I would have to get a list of every non-alphabet / non-number character, then run a SELECT with a LIKE and Wildcard qualifiers.
Here is what I would run:
SELECT Col1
FROM TABLE
WHERE Col1 LIKE ('!', '#', '#', '$', '%'....)
However, I don't think you can run multiple qualifiers, can you? Is there a way I could accomplish this?
Negatives are your friend here:
SELECT Col1
FROM TABLE
WHERE Col1 like '%[^a-Z0-9]%'
Which says that you want any rows where Col1 consists of any number of characters, then one character not in the set a-Z0-9, and then any number of characters.
If you have a case sensitive collation, it's important that you use a range that includes both upper and lower case A, a, Z and z, which is what I've given (originally I had it the wrong way around. a comes before A. Z comes after z)
Or, to put it another way, you could have written your original WHERE as:
Col1 LIKE '%[!##$%]%'
But, as you observed, you'd need to know all of the characters to include in the [].
The following transact SQL script works for all languages (international). The solution is not to check for alphanumeric but to check for not containing special characters.
DECLARE #teststring nvarchar(max)
SET #teststring = 'Test''Me'
SELECT 'IS ALPHANUMERIC: ' + #teststring
WHERE #teststring NOT LIKE '%[-!#%&+,./:;<=>#`{|}~"()*\\\_\^\?\[\]\'']%' {ESCAPE '\'}
Select * from TableName Where ColumnName LIKE '%[^A-Za-z0-9, ]%'
This will give you all the row which contains any special character.
select count(*) from dbo.tablename where address_line_1 LIKE '%[\'']%' {eSCAPE'\'}

'Converting varchar to data type numeric' error after successful conversion to decimal(18,2)

I have a temporary table I'm using for parsing, #rp.
#rp contains an nvarchar(max) column, #rp.col8, which holds positive and negative numbers to two decimal places of precision e.g. `1234.26'.
I'm able to run the following query and get out a set of converted values out:
select * from
(
select CONVERT(decimal(18,2),rp.col8) as PARSEAMT
from #rp
where
--#rp filtering criteria
)q
However, when I try to query for PARSEAMT = 0 in the following manner, I get the standard '8114, Error converting data type varchar to numeric.':
select * from
(
select CONVERT(decimal(18,2),col8) as PARSEAMT
from #rp
where
--#rp filtering criteria
)q
where q.PARSEAMT = 0
Without that where clause, the query runs fine and generates the expected values.
I've also tried other clauses like where q.PARSEAMT = 0.00 and where q.PARSEAMT = convert(decimal(18,2),0).
What am I doing wrong in my comparison?
I was going to suggest you select PARSEAMT into another temp-table/table-variable but I can see you've already done that from your comments.
Out of interest what does the following yield?
select
col8
from
#rp
where
-- ISNUMERIC returns 1 when the input expression evaluates to a valid
-- numeric data type; otherwise it returns 0. Valid numeric data types
-- include the following:
isnumeric(col8) <> 1

Resources