How can I use If-Then statements in Snowflake - SnowSQL (Linux) - sql

I' ve been trying to use If-Then statements in Snowflake - SnowSQL but I keep getting the error:
001003 (42000): SQL compilation error:
syntax error line 1 at position 0 unexpected 'IF'.
Here is my code:
#!/bin/ksh
export SNOWSQL_USER="my_usr"
export SNOWSQL_PWD="my_pwd"
export SNOWSQL_WAREHOUSE="my_wh"
export SNOWSQL_DATABASE="my_db"
export SNOWSQL_SCHEMA="my_schm"
export SNOWSQL_ACCOUNT="my_acct"
snowsql -o echo=true -o exit_on_error=True<<EOSQL
-- create a sample table
CREATE TEMPORARY TABLE sample_table (id INT, value INT);
-- populate the sample table
INSERT INTO sample_table VALUES (1, 10), (2, 20), (3, 30);
-- use an IF statement to conditionally update the value column
IF (SELECT COUNT(*) FROM sample_table WHERE value > 25) > 0 THEN
UPDATE sample_table SET value = value * 2 WHERE value > 25;
ELSE
UPDATE sample_table SET value = value * 3;
END IF;
-- view the updated table
SELECT * FROM sample_table;
EOSQL
Am I missing something that's necessary for the if statement to run?
Tried to write it in a different way.. nothing. Tried to use variables.. nothing.

To use control flow constructs like IF-ELSE you need Snowflake Scripting block:
EXECUTE IMMEDIATE $$
BEGIN
IF (EXISTS(SELECT * FROM sample_table WHERE value > 25)) THEN
UPDATE sample_table SET value = value * 2 WHERE value > 25;
ELSE
UPDATE sample_table SET value = value * 3;
END IF;
END;
$$;
Also:
(SELECT COUNT(*) FROM sample_table WHERE value > 25) > 0
=>
EXISTS(SELECT * FROM sample_table WHERE value > 25)
Related: Using Snowflake Scripting in SnowSQL and the Classic Web Interface

Related

Create Temp Table in Each Loop and Union After Loop Completion

Using BigQuery's standard SQL scripting functionality, I want to 1) create a temp table for each iteration of a loop, and 2) union those temp tables after the loop is complete. I've tried something like the following:
DECLARE i INT64 DEFAULT 1;
DECLARE ttable_name STRING;
WHILE i < 10 DO
SET ttable_name = CONCAT('temp_table_', CAST(i AS STRING));
CREATE OR REPLACE TEMP TABLE ttable_name AS
SELECT * FROM my_table AS mt WHERE mt.my_col = 1;
SET i = i + 1;
END LOOP;
SELECT * FROM temp_table_*; -- wildcard table to union all results
But I get the following error:
Exceeded rate limits: too many table update operations for this table.
How can I accomplish this task?
Your script does not work the way you think it does!
Instead of writing in each iteration into separate table named like temp_table_N - you actually writing to the very same temp table named ttable_name - thus the Exceeded rate limits error
BigQuery does not allow using variables for objects names
Don't create new tables. Add to an existing one with an INSERT INTO, or hold data in a variable (if it's not too much data), as in:
DECLARE steps INT64 DEFAULT 1;
DECLARE table_holder ARRAY<STRUCT<steps INT64, x INT64, y ARRAY<INT64>>>;
LOOP
SET table_holder = (
SELECT ARRAY_AGG(
STRUCT(steps, 1 AS x, [1,2,3] AS y))
FROM (SELECT '')
);
SET steps = steps+1;
IF steps=30 THEN LEAVE; END IF;
END LOOP;
CREATE TABLE temp.results
AS
SELECT *
FROM UNNEST(table_holder)
Related: https://stackoverflow.com/a/59314390/132438
Question asker/OP here. While I have selected #felipe-hoffa's answer as I believe it will be best for future readers of this question, I have actually gone a different route in solving my problem:
BEGIN
DECLARE i INT64 DEFAULT 1;
CREATE OR REPLACE TEMP TABLE ttable AS
SELECT
CAST(NULL AS INT64) AS col1 -- cast NULL as the type of target col
,CAST(NULL AS FLOAT64) AS col2
,CAST(NULL AS DATE) AS col3;
WHILE i < 10 DO
-- overwrite `ttable` with its previous contents union'ed
-- with new data results from current loop iteration
CREATE OR REPLACE TEMP TABLE ttable AS
SELECT mt.col1, mt.col2, mt.col3 FROM my_table AS mt WHERE mt.other_col = i
UNION ALL
SELECT * FROM ttable;
SET i = i + 1;
END LOOP;
SELECT * FROM ttable; -- UNION'ed results
DROP TABLE IF EXISTS ttable;
END;
Why? I find it easier to stay in "table land" than to venture into "STRUCT/ARRAY land".

Store result of a query inside a function

I've the following function:
DO
$do$
DECLARE
maxgid integer;
tableloop integer;
obstacle geometry;
simplifyedobstacle geometry;
BEGIN
select max(gid) from public.terrain_obstacle_temp into maxgid;
FOR tableloop IN 1 .. maxgid
LOOP
insert into public.terrain_obstacle (tse_coll,tse_height,geom) select tse_coll,tse_height,geom
from public.terrain_obstacle_temp where gid = tableloop;
END LOOP;
END
$do$;
I need to modify this function in order to execute different queries according to the type of a column of public.terrain_obstacle_temp.
This is a temporary table created by reading a shapefile, and I need to know the kind of the geom column of that table. I have a query that give the data to me:
SELECT type
FROM geometry_columns
WHERE f_table_schema = 'public'
AND f_table_name = 'terrain_obstacle'
and f_geometry_column = 'geom';
It returns me a character_varying value (in this case MULTIPOLYGON).
Ho can I modify the function in order to get the result of the query, and create an if statement that allows me to execute some code according to the result of that query?
Is the intention to copy all the records from the temp table to the actual table? If so, you may be able to skip the loop:
insert into public.terrain_obstacle (tse_coll, tse_height, geom)
select tse_coll, tse_height, geom
from public.terrain_obstacle_temp
;
Do terrain_obstacle and terrain_obstacle_temp have the same structure? If so, then the "insert into ... select ..." should work fine provided the column types are the same.
If conditional typing is required, use the CASE WHEN syntax:
v_type geometry_columns.type%TYPE;
...
SELECT type
INTO v_type
FROM geometry_columns
WHERE f_table_schema = 'public'
AND f_table_name = 'terrain_obstacle'
AND f_geometry_column = 'geom'
;
insert into public.terrain_obstacle (tse_coll, tse_height, geom)
select tse_coll
,tse_height
,CASE WHEN v_type = 'MULTIPOLYGON' THEN my_func1(geom)
WHEN v_type = 'POINT' THEN my_func2(geom)
ELSE my_default(geom)
END
from public.terrain_obstacle_temp
;

Facing issue while creating a simple DB2 trigger

Tried to create following trriger:
CREATE TRIGGER EWR.INS_STU
AFTER INSERT ON EWR.STUDENT
FOR EACH ROW
BEGIN
IF ( :NEW.ROLL_NO > 60 ) THEN
INSERT INTO EWR.STUDENT_DIV VALUES ( :NEW.ROLL_NO,'P');
END IF;
IF( :NEW.ROLL_NO < 60)
THEN
INSERT INTO EWR.STUDENT_DIV VALUES (:NEW.ROLL_NO,'F');
END IF;
END
!
But it is giving the following error:
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0104N An unexpected token ":NEW.ROLL_NO > 30 ) THEN INSERT INT" was
found following "H ROW BEGIN IF (". Expected tokens may include:
"". LINE NUMBER=6. SQLSTATE=42601
SQL0104N An unexpected token ":NEW.ROLL_NO > 30 ) THEN
INSERT INT" was found following "H ROW
BEGIN
IF (". Expected tokens may include: "".
Why are you using NEW with the notation as a host variable (:)? You do not need to put the colon before the variable name, because this is a trigger that uses only sql (sql pl).
Also, you have to declare how you are going to reference the new values, defined in the header.
REFERENCING NEW AS N
I recreated your case, and it works for me like this:
db2 "create table ewr.student(roll_no int)"
db2 "create table ewr.student_div(roll_no int, other char(1))"
trigger.sql
CREATE TRIGGER EWR.INS_STU
AFTER INSERT ON EWR.STUDENT
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
IF ( NEW.ROLL_NO > 60 ) THEN
INSERT INTO EWR.STUDENT_DIV VALUES ( NEW.ROLL_NO, 'P' );
END IF;
IF ( NEW.ROLL_NO < 60 ) THEN
INSERT INTO EWR.STUDENT_DIV VALUES ( NEW.ROLL_NO, 'F' );
END IF;
END !
db2 -td! -f trigger.sql
DB20000I The SQL command completed successfully.
I hope this solve your problem.

Error on executing trigger

CREATE OR REPLACE TRIGGER PROCESS_POPULATE_INSTANCE
AFTER INSERT OR UPDATE ON PROCESS_INSTANCE
FOR EACH ROW
DECLARE
InstanceExists NUMBER;
BEGIN
SELECT COUNT(*)
INTO InstanceExists
FROM TEST_PROCESSDATA
WHERE TEST_PROCESSDATA.PROCESS_INSTANCE_ID = :NEW.INSTANCE_ID ;
IF ( InstanceExists > 0 ) THEN
UPDATE TEST_PROCESSDATA SET PROCESS_STATUS =:NEW.STATUS WHERE PROCESS_INSTANCE_ID = NEW.INSTANCE_ID;
ELSIF
INSERT INTO TEST_PROCESSDATA (PROCESS_INSTANCE_ID,PROCESS_STATUS, STARTED_TIME) VALUES (:NEW.INSTANCE_ID,:NEW.STATUS,:NEW.START_TIME);
END IF;
END PROCESS_POPULATE_APPDATA;
On executing the above trigger, i get the below error:
Error(12,2): PLS-00103: Encountered the symbol "INSERT" when expecting one of the following: ( - + case mod new not null continue avg count current exists max min prior sql
stddev sum variance execute forall merge time timestamp interval
date
pipe
Error(13,2): PLS-00103: Encountered the symbol "END"
In addition to what Ravindra bagale has already noted I would add the following:
First. In the statement
UPDATE TEST_PROCESSDATA SET PROCESS_STATUS =:NEW.STATUS
WHERE PROCESS_INSTANCE_ID = NEW.INSTANCE_ID;
: colon is missing in front of NEW.INSTANCE_ID
And second. You might consider using of merge statement instead of IF .. THEN .. ELSE.. END IF construct and additional select statement. For example.
create or replace trigger process_populate_instance
after insert or update on process_instance
for each row
begin
merge into test_processdata
using dual
on (process_instance_id = :new.instance_id)
when matched
then update
set process_status =:new.status
when not matched
then insert (process_instance_id,process_status, started_time)
values (:new.instance_id,:new.status,:new.start_time);
end;
use ELSE instead of ELSIF
here u used elseif,but not used else, u can't use elseif without else
when there are more than two way then u can use elsif.
IF ( InstanceExists > 0 ) THEN
UPDATE TEST_PROCESSDATA SET PROCESS_STATUS =:NEW.STATUS WHERE PROCESS_INSTANCE_ID = NEW.INSTANCE_ID;
ELSE
INSERT INTO TEST_PROCESSDATA (PROCESS_INSTANCE_ID,PROCESS_STATUS, STARTED_TIME) VALUES (:NEW.INSTANCE_ID,:NEW.STATUS,:NEW.START_TIME);
END IF;
END PROC

PostgresQL: Loop over integer[] and insert current element into another table

I have a function which takes a parameter named p_categories, of type smallint[].
How do I look through p_categories and execute the following command, where cat is the current smallint in p_categories?
INSERT INTO mytable (i_category) VALUES (cat)
Something like this (pseudocode) maybe?
FOR cat in SELECT p_categories
INSERT INTO mytable (i_category) VALUES (cat)
END LOOP;
That gives me an error: "invalid input syntax for integer: "{14,20}" when p_categories is '{14,20}'.
I think you're looking for unnest
INSERT INTO mytable (i_category)
SELECT unnest(p_categories);
The unnest array function just expands an array into its elements.
Or a more concrete example:
> create table t (i int not null);
> insert into t (i) select unnest(array[1,2]);
> select * from t;
i
---
1
2
(2 rows)