I have a Snowflake SQL procedure, which calls a stored procedure, the calling procedure "PROC_TABLE_COUNT" goes in a for loop dose some processing and calls another procedure "getRowCount" which captures the row count in a specific schema / table.
How to call the procedure and capture the value returned by the procedure.
A sample code for both the procedures is as follows.
CREATE OR REPLACE PROCEDURE PROC_TABLE_COUNT()
returns varchar(20000)
language sql
execute as caller
as
$$
declare
v_err_stmt varchar2;
v_sqlerrm varchar2;
v_src_schema varchar2;
v_tgt_schema varchar2;
v_sis_table varchar2;
v_census_table varchar2;
v_select_stmt varchar2;
v_insert_stmt varchar2;
--RCN count
v_sis_count integer;
v_census_count integer;
v_select_RCN_stmt varchar2;
begin
v_src_schema := 'SCHEMA_1';
v_sis_count := getRowCount('SCHEMA_1','TABLE_1');
v_err_stmt := v_select_RCN_stmt;
-- The row count will be captured and inserted into a table.
end;
$$;
create or replace procedure getRowCount(schema_name varchar, table_name varchar)
returns table(a integer)
language sql
as
$$
declare
res RESULTSET;
query varchar default 'SELECT count(*) FROM ' || :schema_name || '.' || :table_name ;
begin
res := (execute immediate :query);
return table (res);
end;
$$;
In place of below in procedure code - PROC_TABLE_COUNT() -
v_sis_count := getRowCount('SCHEMA_1','TABLE_1');
v_err_stmt := v_select_RCN_stmt;
Use following -
call getRowCount('PUBLIC','TABLE_1');
v_err_stmt := (select * from table(result_scan(last_query_id())) );
insert into TABLE_1 values (:v_err_stmt); - or whatever table name
Related
I am trying to parse JSON value and getting Syntax Error in stored procedure.
This SELECT statement works fine:
SELECT
parse_json ('{"fName":"Pink","lName":"Panther"}') AS json_data,
json_data:fName::string AS first_name,
json_data:lName::string AS last_name;
While trying same thing in stored procedure, I am getting a syntax error:
CREATE OR REPLACE PROCEDURE extract_json(input_json varchar)
RETURNS TABLE (res varchar)
LANGUAGE SQL
AS
$$
DECLARE
qry string;
res resultset;
BEGIN
qry := 'SELECT parse_json('||:input_json||') AS json_data::sting, json_data:fName::string';
res := (execute immediate qry);
return table(res);
END;
$$
;
CALL extract_json('{"fName":"Pink","lName":"Panther"}');
Expected out, 2 columns only:
FIRST_NAME LAST_NAME
Pink Panther
Any help is appreciated.
Thanks
The are few issues with the code:
a) resulset contains two column so (res varchar) will not work
b) alias json_data::sting cannot be casted
c) missing ' around input_json, ideally it should be bind parameter
CREATE OR REPLACE PROCEDURE extract_json(input_json varchar)
RETURNS TABLE (col VARIANT, res varchar)
LANGUAGE SQL
AS
$$
DECLARE
qry string;
res resultset;
BEGIN
qry := 'SELECT parse_json('''||:input_json||''') AS json_data, json_data:fName::string';
res := (execute immediate qry);
return table(res);
END;
$$
;
CALL extract_json('{"fName":"Pink","lName":"Panther"}');
Output:
Expected out, 2 columns only:
CREATE OR REPLACE PROCEDURE extract_json(input_json varchar)
RETURNS TABLE (firstName VARCHAR, lastName varchar)
LANGUAGE SQL
AS
$$
DECLARE
qry string;
res resultset;
BEGIN
qry := 'SELECT json_data:fName::string, json_data:lName::string FROM (SELECT parse_json('''||:input_json||''') AS json_data)';
res := (execute immediate qry);
return table(res);
END;
$$;
CALL extract_json('{"fName":"Pink","lName":"Panther"}');
Output:
I need to run the query which is saved in a field in one table and insert the value of query output into another table.
To simplify the ask I have created dummy tables and try to test the logic.
Table 1
create table tsql(id string, q string);
insert into tsql values (1,'Select current_date');
Table tsql has select query in field name q.
Table 2
create table tinput(d date);
Table 2 will get updated from the value in table 1.
Below are the stored procedure I am trying to write. I know this can be done in javascript stored proc but I need to write this in sql as we are following SQL for all other.
Procedure so far.
create or replace procedure sqlreadwrite(id string)
returns string
language sql
as
$$
Declare
select_statement String;
begin
create or replace temp table tk
as select q from tsql where id = :id;
--select_statement := 'insert into tinp values (execute immediate 'Select Q from tk')';
execute immediate 'insert into tinp values (execute immediate 'Select Q from tk')';
return 'Success';
end;
$$;
Till now it is failing.
you don't need to call execute immediate twice, you should call it only once. One thing i wee is you have not specified the list of columns in you INSERT statement , it is always better to specify the list of column.
-- untested
execute immediate 'insert into tinp values (Select Q from tk)';
I figured out a way, however I am open to suggestion and better approach.
Below is the query.
create or replace procedure sqlread(id string)
returns string
language sql
as
$$
Declare
select_statement String;
res resultset;
value string;
res1 resultset;
v string;
begin
create or replace temp table tk as select q from tsql where id = :id;
select_statement := 'Select Q from tk';
res := (execute immediate :select_statement);
let c1 cursor for res;
for row_variable in c1 do
value := row_variable.Q;
res1 := (execute immediate :value);
let c1 cursor for res1;
for row_variable1 in c1 do
v := row_variable1.current_date::date;
insert into tinp values (:v);
end for;
End For;
return v;
end;
$$;
If the target table (tinput) will only accept dates, I wonder what kind of queries are stored in the source table (tsql)? Anyway, I assume there is a business requirement behind this, so your approach should be OK.
Some improvements:
You may remove the TEMP table as it doesn't provide any benefits.
If your procedure will only run 1 query from tinput (if IDs are unique), then you don't need a loop.
Here is the sample code:
create or replace procedure sqlread(p_id string)
returns string
language sql
as
$$
Declare
select_statement String;
res resultset;
value string;
res1 resultset;
v string;
begin
select_statement := 'select q from tsql where id = ?';
res := (execute immediate :select_statement using (P_ID));
let c1 cursor for res;
open c1;
fetch c1 into value;
res1 := (execute immediate :value);
let c2 cursor for res1;
open c2;
fetch c2 into value;
insert into tinput values (:value);
return :value;
exception
when other then
return 'error';
end;
$$;
I try to run in snowflake the following sql stored procedure with a declare variable inside but i got the following error : Error: Bind variable for object MYTABLE AS MYTABLE not set (line 13).
when I hard code the value into the function identifier it's work by the way...
CREATE OR REPLACE PROCEDURE DBNAME.SCHEMANAME."SP_test"()
RETURNS varchar
LANGUAGE SQL
EXECUTE AS CALLER
AS
declare
MYTABLE varchar := 'DBNAME.SCHEMANAME.TABLENAME';
--MYRESULT varchar;
BEGIN
-- let MYTABLE varchar := 'DBNAME.SCHEMANAME.TABLENAME';
-- MYTABLE := 'DBNAME.SCHEMANAME.TABLENAME';
--filter to return 1 row
-- let MYRESULT varchar := ( select col1 from IDENTIFIER( 'DBNAME.SCHEMANAME.TABLENAME' ) where col2=2 ) ;
--ko
let MYRESULT varchar := ( select col1 from IDENTIFIER( :MYTABLE ) where col2=2 ) ;
return :MYRESULT;
end;
This works for me:
CREATE OR REPLACE PROCEDURE "SP_test"()
RETURNS varchar
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
declare
MYRESULT resultset;
MYTABLE varchar := 'SERGIU_TESTDB.PUBLIC.CITIBIKE_TRIPS';
MYVAL varchar;
BEGIN
MYRESULT := (select ride_id from identifier(:MYTABLE) limit 1);
let c1 cursor for MYRESULT;
for row_variable in c1 do
MYVAL := row_variable.ride_id;
end for;
return MYVAL;
END
$$;
Calling it:
CALL "SP_test"();
I get a value out of it:
B1CE81D802D68DF8
Can we return a table in Snowflake using sql as language? What is the correct syntax if possible?
create or replace procedure sp()
returns table ()
language sql
as
$$
declare
accountingMonth :=(select dateadd(month,case when day(current_timestamp())<=10 then -1 else 0 end,dateadd(month,0,date_trunc('month',current_date()))));
endmonth :=(select dateadd(month,1,dateadd(month,case when day(current_timestamp())<=10 then -1 else 0 end,dateadd(month,0,date_trunc('month',current_date())))));
currmonth date default accountingMonth;
begin
create or replace temporary table sa as (
select col1,col2, col3,datecol,....col270
from table2
where datecol = :accountingmonth
);
end;
select * from sa;
$$
Yes, it is possible to return table from strored procedure using RESULTSET
CREATE OR REPLACE PROCEDURE sp()
RETURNS TABLE(col1 INTEGER, ...)
LANGUAGE SQL
AS
BEGIN
-- ...
let RESULTSET DEFAULT (select * from sa);
RETURN TABLE(res);
END;
Is there a way to build a select statement in oracle using values stored in variables?
For example can you do this:
declare
tbl_var varchar2(10) := "map_set";
begin
select count(*) from tbl_var;
end;
Yes there is, using execute immediate:
declare
tbl_var varchar2(10) := 'map_set';
result number;
begin
execute immediate 'select count(*) from '||tbl_var into result; --save result into variable
dbms_output.put_line('Total rows:'||result); --print result
end;
Second way, you can create a function that receives table name as parameter and return the count:
create function get_count(tbl_var varchar2) return number is
result number;
begin
execute immediate 'select count(*) from '||tbl_var into result;
return result;
end;
After create the function you can query it like this:
select get_count('map_set') from dual;