I am trying to create a Snowflake equivalent to the below T-SQL based adhoc query.
**T-SQL version**
Declare #i int = 0;
If(#i = 0)
PRINT '0';
Else
Begin
PRINT '1'
RETURN;
PRINT '2'
End
**Snowflake version**
Set i = 0;
If($i = 0)
Select '0';
Else
Begin
Select '1'
RETURN;
Select '2'
End
When I am running the Snowflake query from the Snowflake Web UI, I am getting an error as
SQL compilation error : syntax error line 1 at position 0 unexpected 'IF'
I have searched the snowflake documentation and did not find helpful documentation for:
If Else
Begin End
Return
Any help would be appreciated.
Thank you.
Snowflake does not support conditional T-SQL statements. It does support conditional expressions within a query.
For conditional statement execution, consider using Snowflake's stored procedures with embedded JavaScript if the use of conditional expressions is inadequate for your needs.
A very trivial example of a stored procedure, illustrating a conditional query execution upon call:
CREATE OR REPLACE PROCEDURE stored_proc_example(TEST_VALUE INTEGER)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
// Declaring SQL statement(s) to execute later
var command_1 = "SELECT 1";
var command_2 = "SELECT 2";
if (TEST_VALUE === 0) {
var stmt = snowflake.createStatement({ sqlText: command_1 });
var rs = stmt.execute();
// Omitted: Perform logic with 'rs' rows to prepare a return value
return '1';
} else {
var stmt = snowflake.createStatement({ sqlText: command_2 });
var rs = stmt.execute();
// Omitted: Perform logic with 'rs' rows to prepare a return value
return '2';
}
$$
;
Run as:
SET i=0;
CALL stored_proc_example($i);
SET i=1;
CALL stored_proc_example($i);
Snowflake Scripting introduced anonymous block and Branching constructs:
DECLARE
i INT DEFAULT 0;
BEGIN
IF (i = 0) THEN
RETURN 'zero';
ELSE
RETURN 'non-zero';
END IF;
END;
or:
BEGIN
LET i INT := 0;
IF (i = 0) THEN
RETURN 'zero';
ELSE
RETURN 'non-zero';
END IF;
END;
Further reading: Working with Variables
In Snowflake you can use CASE WHEN statements for this. Here's the documentation.
You can try with snowflake scripting and change your accordingly
set
stmt = $$
begin
let i := 1;
case when (i=0) then
return 0;
when (i=1 ) then
return '1 ';
else
return '2';
end ;
end;
$$;
execute immediate $stmt;
SELECT student_name 'Student Name',
CASE grade
WHEN 'A' THEN 'Excellent'
WHEN 'B' THEN 'Good'
WHEN 'C' THEN 'Average'
WHEN 'D' THEN 'Poor'
WHEN 'F' THEN 'Fail'
ELSE 'N/A'
END 'Grade'
FROM student
-- Output
Related
I'm trying to create a Snowflake stored procedure, similar to the below code. But the like operator in the if-else block is not working.
Stored procedure
create or replace procedure test_stored_procedure(inputvalue varchar)
returns string not null
language javascript
as
$$
if (inputvalue like '%abc%') { return 'valid input' } else { return 'invalid input'}
$$
;
I am getting the below error while calling the procedure
JavaScript compilation error: Uncaught SyntaxError: Unexpected identifier in test_stored_procedure at ' if (inputvalue like '%abc%') { return 'valid input' } else { return 'invalid input'}' position 14
JavaScript equivalent of LIKE '%abc%' is includes('abc'):
create or replace procedure test_stored_procedure(inputvalue VARCHAR)
returns string not null
language javascript
as
$$
if ( INPUTVALUE.includes('abc')) {
return 'valid input';
}
else {
return 'invalid input';
}
$$;
Test:
CALL TEST_STORED_PROCEDURE('fabc');
-- valid input
CALL TEST_STORED_PROCEDURE('xyz');
-- invalid input
Notes:
Statements should be terminated with ; and
Case-sensitivity in JavaScript Arguments:
For stored procedures (and UDFs) that use JavaScript, identifiers (such as argument names) in the SQL portion of the statement are converted to uppercase automatically (unless you delimit the identifier with double quotes), while argument names in the JavaScript portion will be left in their original case
I'm looking for some help diagnosis what I'm doing wrong, when applying a working RegEx expression to Snowflake (specifically REGEXP_REPLACE()).
I'm trying to replace commas (not within a quoted section) with another string. I've tested and confirmed that the expression returns the desired result in regex101.com, but when I try and apply it to a Snowflake query I'm not getting any results.
I've seen references in the REGEXP_REPLACE() documentation (indicating the need for additional escapes on brackets) which I have applied - still no dice.
Can anyone tell me what I'm missing??
Sample text (C1):
99999999999,"SOME CORPROATION, Dissolved January 17, 1983",123 SOME STREET #760,,Denver,CO,90210,,,,,,,,Voluntarily Dissolved,CO,Corporation,JOHN,F.,DOE,,,1512 SOME STREET #760,,DENVER,CO,90210,US,,,,,,,03/29/1886
Working Regex:
(?:[^"\']|(?:\".*?\")|(?:\'.*?\'))*?(,)
My interpretation of SF reqs for RegEx:
REGEXP_REPLACE((C1), '\\(?:[^\\"\']|\\(?:\".*?\"\\)|\\(?:\'.*?\'\\)\\)*?\\(,\\)', '","') AS "blah"
just discovered that Snowflake only offers support for Posix Standard and Extended RegEx, so usage of non-capturing groups is not possible at all.
If you just want a solution that works rather than specifically a REGEX solution, then the following UDF should do the job:
CREATE OR REPLACE FUNCTION replace_char("in_text" string, "replace_text" string, "skip_text" string)
RETURNS string
LANGUAGE JAVASCRIPT
AS
$$
var out_string = '';
var skipping = false;
for (var i = 0; i < in_text.length; i++) {
if (in_text.charAt(i) == skip_text) {
skipping = !skipping;
}
if (skipping === false && in_text.charAt(i) != replace_text) {
out_string = out_string + in_text.charAt(i);
}
else {
if (skipping === true) {
out_string = out_string + in_text.charAt(i);
}
}
}
return out_string;
$$
;
In order to be productionised it would need error handling, checks on the inputs, etc. but this should be enough to get you started.
You can use it as follows:
set intext = '99999999999,"SOME CORPROATION, Dissolved January 17, 1983",123 SOME STREET #760,,Denver,CO,90210,,,,,,,,Voluntarily Dissolved,CO,Corporation,JOHN,F.,DOE,,,1512 SOME STREET #760,,DENVER,CO,90210,US,,,,,,,03/29/1886';
set replace_text = ','; -- char to remove from $intext
set skip_text = '"'; --Between matching occurrences of this char no text will be replaced
select replace_char($intext,$replace_text,$skip_text);
I'm trying to return a variable with a PostgreSQL function that returns row/rows so I can use libpqxx on the client side to iterate over it for example using:
for (pqxx::result::const_iterator row = result.begin(); row != result.end(); row++)
{
for (pqxx::const_row_iterator field = row.begin(); field != row.end(); field++)
{
cout << field << '\n';
}
}
This is my PostgresSQL function:
CREATE OR REPLACE FUNCTION seal_diff_benchmark_pgsql(sealparams CHARACTER VARYING) RETURNS RECORD AS $outputVar$
DECLARE
tempVar1 CHARACTER VARYING;
tempVar2 CHARACTER VARYING;
outputVar1 TEXT[];
outputVar record;
sealArray TEXT[];
execTime NUMERIC[];
BEGIN
FOR i IN 1..2 LOOP
SELECT "Pickup_longitude", "Dropoff_longitude" INTO tempVar1, tempVar2 FROM public.nyc2015_09_enc WHERE id=i;
sealArray := (SELECT public.seal_diff_benchmark(tempVar1, tempVar2, sealparams));
outputVar1[i] := sealArray[1];
execTime[i] := sealArray[2];
END LOOP;
SELECT UNNEST(outputVar1) INTO outputVAR;
RETURN outputVar;
END;
$outputVar$ LANGUAGE plpgsql;
I also tried returning outputVar1 as TEXT[]. My field variable on the client side holds {foo, bar} if I use returns TEXT[] or (foo) if I use returns RECORD. But this is not what I need, which is a row like return from a TEXT[] array or a RECORD variable without any (), [], {} chars at the beginning and at the end of the output.
How can I change my PostgreSQL function to make it work? I think I'm missing something but I can't see what.
There are many approaches to do what you want.
If it really is just one column that you want, then you can simply do:
CREATE OR REPLACE FUNCTION seal_diff_benchmark_pgsql(sealparams CHARACTER VARYING)
RETURNS SETOF TEXT AS $outputVar$
DECLARE
tempVar1 CHARACTER VARYING;
tempVar2 CHARACTER VARYING;
sealArray TEXT[];
execTime NUMERIC[];
outputVar text;
BEGIN
FOR i IN 1..2 LOOP
SELECT "Pickup_longitude", "Dropoff_longitude" INTO tempVar1, tempVar2
FROM public.nyc2015_09_enc WHERE id=i;
sealArray := (SELECT public.seal_diff_benchmark(tempVar1, tempVar2, sealparams));
execTime[i] := sealArray[2];
FOREACH outputVar IN ARRAY sealArray[1] LOOP --iterate over that text array
RETURN NEXT outputVar;
END LOOP;
END LOOP;
END;
$outputVar$ LANGUAGE plpgsql;
Returned colum will be named just like the function.
SELECT seal_diff_benchmark_pgsql FROM seal_diff_benchmark_pgsql('stuff');
-- alternative
SELECT seal_diff_benchmark_pgsql('stuff');
You can also specify columns in function parameters:
CREATE OR REPLACE FUNCTION seal_diff_benchmark_pgsql(sealparams CHARACTER VARYING, OUT outputVar text)
Then returned column will be named outputVar. In case of returning just one column, Postgres forces RETURNS to be of that column type, so in this case SETOF TEXT or just TEXT if one row is expected. If you return more than one column, then you need to use RETURNS SETOF RECORD.
When you use named columns in function parameters, then you need to assign values to them just like you would to variables from DECLARE section:
LOOP
outputVar := 'some value';
outputVar2 := 'some value';
outputVar3 := 'some value';
RETURN NEXT;
END LOOP;
There are a few other examples on how to return sets from functions in my old answer here: How to return rows of query result in PostgreSQL's function?
create or replace trigger emp_trig
before insert or update of salary on emp
for each row
when `REGEXP_LIKE(:new.job_id, 'ac*','i')` -- Here
BEGIN
IF inserting then
:new.commission_pct := 0.20;
elsif (:old.commission_pct is null) then
:new.commission_pct := 0.1;
END IF;
END;
create or replace trigger emp_trig
before insert or update of salary on emp
for each row
when (REGEXP_LIKE(new.job_id, 'ac*','i'))
BEGIN
IF inserting then
:new.commission_pct := 0.20;
elsif (:old.commission_pct is null) then
:new.commission_pct := 0.1;
END IF;
END;
/
Hey. if you are trying to do a simple match then avoid using Regular
expression. Instead go with LIKE and your test condition. Below
snippet illustrates a simple example to suffice your requirement. Hope
it helps
CREATE OR REPLACE TRIGGER emp_trig before
INSERT OR
UPDATE OF sal ON emp FOR EACH row
WHEN (new.job LIKE '%TEST%')
DECLARE
BEGIN
IF inserting THEN
:new.comm := 0.20;
elsif (:old.comm IS NULL) THEN
:new.comm := 0.1;
END IF;
END;
I have a String stored in a table in the following key-value format: "Key1☺Value1☺Key2☺Value2☺KeyN☺ValueN☺".
Given a Key how can I extract the Value? Is regex the easiest way to handle this? I am new to PL/SQL as well as Regex.
In this case, I would use just a regular split and iterate through the resulting array.
public string GetValue(string keyValuePairedInput, string key, char separator)
{
var split = keyValuePairedInput.Split(separator);
if(split.Lenght % 2 == 1)
throw new KeyWithoutValueException();
for(int i = 0; i < split.Lenght; i += 2)
{
if(split[i] == key)
return split[i + 1];
}
throw new KeyNotFoundException();
}
(this was not compiled and is not pl/sql anyway, treat it as pseudocode ☺)
OK I hear your comment...
Making use of pl/sql functions, you might be able to use something like this:
select 'key' as keyValue,
(instr(keyValueStringField, keyValue) + length(keyValue) + 1) as valueIndex,
substr(keyValueStringField, valueIndex, instr(keyValueStringField, '\1', valueIndex) - valueIndex) as value
from Table
For this kind of string slicing and dicing in PL/SQL you will probably have to use regular expressions. Oracle has a number of regular expression functions you can use. The most commonly used one is REGEXP_LIKE which is very similar to the LIKE operator but does RegEx matching.
However you probably need to use REGEXP_INSTR to find the positions where the separators are then use the SUBSTR function to slice up the string at the matched positions. You could also consider using REGEXP_SUBSTR which does the RegEx matching and slicing in one step.
As an alternative to regular expressions...
Assuming you have an input such as this:
Key1,Value1|Key2,Value2|Key3,Value3
You could use some PL/SQL as shown below:
FUNCTION get_value_by_key
(
p_str VARCHAR2
, p_key VARCHAR2
, p_kvp_separator VARCHAR2
, p_kv_separator VARCHAR2
) RETURN VARCHAR2
AS
v_key VARCHAR2(32767);
v_value VARCHAR2(32767);
v_which NUMBER;
v_cur VARCHAR(1);
BEGIN
v_which := 0;
FOR i IN 1..length(p_str)
LOOP
v_cur := substr(p_str,i,1);
IF v_cur = p_kvp_separator
THEN
IF v_key = p_key
THEN
EXIT;
END IF;
v_key := '';
v_value := '';
v_which := 0;
ELSIF v_cur = p_kv_separator
THEN
v_which := 1;
ELSE
IF v_which = 0
THEN
v_key := v_key || v_cur;
ELSE
v_value := v_value || v_cur;
END IF;
END IF;
END LOOP;
IF v_key = p_key
THEN
RETURN v_value;
END IF;
raise_application_error(-20001, 'key not found!');
END;
To get the value for 'Key2' you could do this (assuming your function was in a package called test_pkg):
SELECT test_pkg.get_value_by_key('Key1,Value1|Key2,Value2|Key3,Value3','Key2','|',',') FROM dual