Function name in snowflake - sql

Hi I'm migrating the SQL version ssrs reports to snowflake version in SQL
They are using isdate() function it returns o or 1 as we know , is there any fun in snowflake to work like this??

yes , you can use is_date() function.
for example :
select * from table where is_date(column) ;
also read documentations here : snowflake docs

The IS_DATE function in Snowflake checks for something different than the ISDATE function in SQL Server. Specifically, it checks for whether or not a variant is a date.
-- These fail because the inputs are not data type variant
select is_date('2023-01-06'::date);
select is_date('2023-01-06');
-- The is_date function requires a variant type:
select is_date('2023-01-06'::date::variant);
You can create a UDF to get the same behavior as the SQL Server ISDATE function:
create or replace function ISDATE(DT string)
returns integer
language sql
as
$$
iff(try_to_date(DT::string) is null, 0, 1)
$$;
select isdate('2023-01-06'); -- Returns 1
select isdate('2023-14-02'); -- Returns 0
select isdate('2017'); -- Returns 1
select isdate('3/4/2012') -- Returns 1

Using TRY_TO_DATE and explicit casting from BOOLEAN to INT:
SELECT col,
TRY_TO_DATE(col) AS COL_AS_DATE,
(TRY_TO_DATE(col) IS NOT NULL)::INT AS ID_DATE_COL
FROM (VALUES ('a'), ('0'), ('2023-01-01')) sub(col);
Output:

Related

how to transform a number in binary representation to a Snowflake number

I have a number in binary (base-2) representation:
"10100110"
How can I transform it to a number in Snowflake?
Snowflake does not provide number or Integer to Binary function out of the box, however these UDF function can be used instead
I also overloaded the UDF in the event a string gets passed.
CREATE OR REPLACE FUNCTION int_to_binary(NUM VARIANT)
RETURNS string
LANGUAGE JAVASCRIPT
AS $$
return (NUM >>> 0).toString(2);
$$;
CREATE OR REPLACE FUNCTION int_to_binary(NUM STRING)
RETURNS string
LANGUAGE JAVASCRIPT
AS $$
return (NUM >>> 0).toString(2);
$$;
I tried with a SQL pure UDF - it worked at first, but not when using it with data over a table.
So I had to create a Javascript UDF:
create or replace function bin_str_to_number(a string)
returns float
language javascript
as
$$
return parseInt(A, 2)
$$
;
select bin_str_to_number('110');
For the record, this is the error I got when attempting a pure SQL UDF for the same:
SQL compilation error: Unsupported subquery type cannot be evaluated
The UDF:
create or replace function bin_str_to_number(a string)
returns number
as
$$
(select sum(value::number*pow(2,index))::number
from table(flatten(input=>split_string_to_char(reverse(a)))))
$$
That was a fun challenge for this morning! If you want to do it in pure SQL:
with binary_numbers as (
select column1 as binary_string
from (values('10100110'), ('101101101'), ('1010011010'), ('1011110')) tab
)
select
binary_string,
sum(to_number(tab.value) * pow(2, (tab.index - 1))) decimal_number
from
binary_numbers,
table(split_to_table(trim(replace(replace(reverse(binary_numbers.binary_string), '1', '1,'), '0', '0,' ), ','), ',')) tab
group by binary_string
Produces:
BINARY_STRING
DECIMAL_NUMBER
10100110
166
101101101
365
1010011010
666
1011110
94

Has anybody created a Snowflake User Defined Function (SQL) that takes decimal numbers as arguments?

I am trying to create a SQL UDF that takes a decimal number as an argument but if I pass in a number with a different number of decimal places I get an error, even if the number has a smaller number of digits to the right of the decimal point.
Here is a example of the problem:
CREATE OR REPLACE TABLE EXAMPLE (
COL1 NUMBER(7,5)
);
INSERT INTO EXAMPLE VALUES (9.12),(10.467),(11.6),(12.01234);
CREATE OR REPLACE FUNCTION LESS_THAN(DATA1 NUMBER(7,5))
RETURNS TABLE (COL1 NUMBER(7,5))
as
$$
SELECT COL1 FROM EXAMPLE WHERE COL1 < DATA1
$$
;
select * from table(LESS_THAN(11.3)); --Error: SQL compilation error: error line 1 at position 20 Invalid argument types for function 'LESS_THAN': (NUMBER(3,1))
select * from table(LESS_THAN(9.85)); --Error: SQL compilation error: error line 1 at position 20 Invalid argument types for function 'LESS_THAN': (NUMBER(3,2))
select * from table(LESS_THAN(10.85001)); --Works
I would like to be able to create a UDF that allows any decimal to be accepted.
Any thoughts?
Thanks
Try casting the inputs to NUMBER(7,5). For example:
select * from table(LESS_THAN('11.3'::NUMBER(7,5)));
select * from table(LESS_THAN( 11.3 ::NUMBER(7,5)));
You can use this system function to observe the types:
select 11.3 x, system$typeof(x); -- NUMBER(3,1)
select 11.30 x, system$typeof(x); -- NUMBER(3,1)
select 11.30 ::NUMBER(7,5) x, system$typeof(x); -- NUMBER(7,5)
select '11.30'::NUMBER(7,5) x, system$typeof(x); -- NUMBER(7,5)

How to fix input is out of range error in Postgres 9.1

In Postgres 9.1 code below produces error
ERROR: input is out of range
CONTEXT: SQL function "gc_dist1" statement 1
How to fix it in 9.1 ?
create or replace function gc_dist1(_lat1 float8, _lon1 float8, _lat2 float8, _lon2 float8)
returns float8 as $$
select ACOS(SIN(radians($1))*SIN(radians($3))+COS(radians($1))*COS(radians($3))*COS(radians($4)-radians($2)))*6371;
$$ language sql immutable;
select gc_dist1(24.6269989013672,59.3357849157094,24.6269989013672,59.3357849121094);
If fails because acos() function cannot accept value higher that 1.
In your example that's the case.
SELECT (
SIN(radians(24.6269989013672))
* SIN(radians(24.6269989013672))
+ COS(radians(24.6269989013672))
* COS(radians(24.6269989013672))
* COS(radians(59.3357849121094)-radians(59.3357849157094))
) > 1 -- true
It seems your function logic is incorrect.

How to get the first field from an anonymous row type in PostgreSQL 9.4?

=# select row(0, 1) ;
row
-------
(0,1)
(1 row)
How to get 0 within the same query? I figured the below sort of working but is there any simple way?
=# select json_agg(row(0, 1))->0->'f1' ;
?column?
----------
0
(1 row)
No luck with array-like syntax [0].
Thanks!
Your row type is anonymous and therefore you cannot access its elements easily. What you can do is create a TYPE and then cast your anonymous row to that type and access the elements defined in the type:
CREATE TYPE my_row AS (
x integer,
y integer
);
SELECT (row(0,1)::my_row).x;
Like Craig Ringer commented in your question, you should avoid producing anonymous rows to begin with, if you can help it, and type whatever data you use in your data model and queries.
If you just want the first element from any row, convert the row to JSON and select f1...
SELECT row_to_json(row(0,1))->'f1'
Or, if you are always going to have two integers or a strict structure, you can create a temporary table (or type) and a function that selects the first column.
CREATE TABLE tmptable(f1 int, f2 int);
CREATE FUNCTION gettmpf1(tmptable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;
SELECT gettmpf1(ROW(0,1));
Resources:
https://www.postgresql.org/docs/9.2/static/functions-json.html
https://www.postgresql.org/docs/9.2/static/sql-expressions.html
The json solution is very elegant. Just for fun, this is a solution using regexp (much uglier):
WITH r AS (SELECT row('quotes, "commas",
and a line break".',null,null,'"fourth,field"')::text AS r)
--WITH r AS (SELECT row('',null,null,'')::text AS r)
--WITH r AS (SELECT row(0,1)::text AS r)
SELECT CASE WHEN r.r ~ '^\("",' THEN ''
WHEN r.r ~ '^\("' THEN regexp_replace(regexp_replace(regexp_replace(right(r.r, -2), '""', '\"', 'g'), '([^\\])",.*', '\1'), '\\"', '"', 'g')
ELSE (regexp_matches(right(r.r, -1), '^[^,]*'))[1] END
FROM r
When converting a row to text, PostgreSQL uses quoted CSV formatting. I couldn't find any tools for importing quoted CSV into an array, so the above is a crude text manipulation via mostly regular expressions. Maybe someone will find this useful!
With Postgresql 13+, you can just reference individual elements in the row with .fN notation. For your example:
select (row(0, 1)).f1; --> returns 0.
See https://www.postgresql.org/docs/13/sql-expressions.html#SQL-SYNTAX-ROW-CONSTRUCTORS

Oracle: SQL query that returns rows with only numeric values

I have a field (column in Oracle) called X that has values like "a1b2c3", "abc", "1ab", "123", "156"
how do I write an sql query that returns me only the X that hold pure numerical values = no letters? from the example above would be „123“ and „156“
select X
from myTable
where ...??
You can use the REGEXP_LIKE function as:
SELECT X
FROM myTable
WHERE REGEXP_LIKE(X, '^[[:digit:]]+$');
Sample run:
SQL> SELECT X FROM SO;
X
--------------------
12c
123
abc
a12
SQL> SELECT X FROM SO WHERE REGEXP_LIKE(X, '^[[:digit:]]+$');
X
--------------------
123
SQL>
If the only characters to consider are letters then you can do:
select X from myTable where upper(X) = lower(X)
But of course that won't filter out other characters, just letters.
Since Oracle 12c (at least) there has been a built-in function to check whether a character value is numeric: VALIDATE_CONVERSION
select X from myTable where validate_conversion(X as number) = 1
If you use Oracle 10 or higher you can use regexp functions as codaddict suggested. In earlier versions translate function will help you:
select * from tablename where translate(x, '.1234567890', '.') is null;
More info about Oracle translate function can be found here or in official documentation "SQL Reference"
UPD: If you have signs or spaces in your numbers you can add "+-" characters to the second parameter of translate function.
What about 1.1E10, +1, -0, etc? Parsing all possible numbers is trickier than many people think. If you want to include as many numbers are possible you should use the to_number function in a PL/SQL function. From http://www.oracle-developer.net/content/utilities/is_number.sql:
CREATE OR REPLACE FUNCTION is_number (str_in IN VARCHAR2) RETURN NUMBER IS
n NUMBER;
BEGIN
n := TO_NUMBER(str_in);
RETURN 1;
EXCEPTION
WHEN VALUE_ERROR THEN
RETURN 0;
END;
/
The complete list of the regexp_like and other regexp functions in Oracle 11.1:
http://66.221.222.85/reference/regexp.html
In your example:
SELECT X
FROM test
WHERE REGEXP_LIKE(X, '^[[:digit:]]$');
You can use following command -
LENGTH(TRIM(TRANSLATE(string1, '+-.0123456789', '')))
This will return NULL if your string1 is Numeric
your query would be -
select * from tablename
where LENGTH(TRIM(TRANSLATE(X, '+-.0123456789', ''))) is null