Apologies but I can't seem to find documentation on this.
I am passing in two strings converting them to date to find the number of rows.
Its a silly question but how do I pass in parameters to a SQL query for cursor
CREATE OR REPLACE PROCEDURE LOAD_DATA("START_DATE" VARCHAR(12), "END_DATE" VARCHAR(12))
RETURNS FLOAT
LANGUAGE SQL
EXECUTE AS OWNER
AS '
declare
row_count float;
c1 cursor for select etl_year,etl_month,etl_day_of_year,1 as rowx
from dim_etldate
where etl_date >= to_date(start_date,''dd-mm-yyyy'')
and etl_date <= to_date(end_date,''dd-mm-yyyy'');
begin
row_count := 0.0;
open c1;
for rec in c1 do
row_count := row_count + 1;
end for;
close c1;
return row_count;
end;
';
Error is invalid identifier 'START_DATE'
If you use an argument in the SQL statement you need to put a colon : in front of it, like in this example (observe the id argument):
CREATE OR REPLACE PROCEDURE find_invoice_by_id(id VARCHAR)
RETURNS TABLE (id INTEGER, price NUMBER(12,2))
LANGUAGE SQL
AS
DECLARE
res RESULTSET DEFAULT (SELECT * FROM invoices WHERE id = :id);
BEGIN
RETURN TABLE(res);
END;
More information here.
Related
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;
create or replace function f() RETURNS void AS
declare
tableArray text[] := '{"ADDRESS","CONSISTENCY_CHECK","DEPARTMENT_SUPERVISION"}';
tableName CHARACTER VARYING;
value INTEGER ;
BEGIN
FOREACH tableName IN ARRAY tableArray
LOOP
select user_id from tableName where user_id=2631;
if found then
update tableName set user_id=2651 where user_id=2631;
delete from tableName where user_id=2631;
END loop;
end;
here is the error that I get when trying to execute the pgplsql: ERROR syntax error at or near "loop"
There are more issues:
the body of function should be string - you can use apostrophes or more usual and practical $$ custom string separators.
the result of SELECT should not be lost. The clause INTO is missing.
table name should not be a variable - variable cannot be used as table name. In this case you need dynamic SQL - EXECUTE statement.
camel notation for variable names should not be used for SQL language, that is case insensitive
CREATE OR REPLACE FUNCTION f()
RETURNS void AS $$
DECLARE
table_array text[] := '{"ADDRESS","CONSISTENCY_CHECK","DEPARTMENT_SUPERVISION"}';
table_name text;
value integer ;
rc integer
BEGIN
FOREACH table_name IN ARRAY table_array
LOOP
EXECUTE format('SELECT * FROM %I WHERE user_id = $1', table_name) USING 2631;
-- variable FOUND cannot be used for dynamic SQL
GET DIAGNOSTICS rc = ROW_COUNT;
IF rc > 0 THEN
EXECUTE format('UPDATE %I SET user_id = $1 WHERE user_id = $2', table_name) USING 2651, 2631;
EXECUTE format('DELETE %I WHERE user_id = $1', table_name) USING 2631;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
I'm busy trying to rewrite some PostgreSQL stored procedures/functions for SQL Server 2014s TSQL.
I am struggling to return my values from this stored procedure though, this one is just a test but I am trying to return multiple rows of data in this case the for the two variables si_code and co_desc.
I have my procedure as follows (as a test)
if (object_id('p_get_serial')) is not null
drop procedure p_get_serial
go
create procedure p_get_serial(#par01 char(20), #par02 integer)
as
declare
#co_num integer,
#co_desc char(20),
#si_code char(20),
#log char(40)
declare mycur cursor for
select co_num, co_desc
from colours
where co_num <= #par02
open mycur
fetch next from mycur into #co_num,
#co_desc
while ##FETCH_STATUS = 0
begin
set #si_code = ''
select #si_code = si_code
from sitems
where si_co_num = #co_num
set #log = #co_desc + ' ' + #si_code
raiserror(#log,0,1) with nowait
fetch next from mycur into #co_num, #co_desc
end
close mycur deallocate mycur
go
exec p_get_serial #par01 = 'paramater01', #par02 = 10
what is the best way to return my results knowing that there will be several rows?
In T-SQL you do not need to declare a cursor. Just select what you need and it will be available to the client app.
Cursor is Oracle/DB2/PostgreSQL etc way of returning data. SQL Server does not need it.
create procedure p
as
select 1 as a
returns a recordset containing one record with one column.
create procedure p
as
select 1 as a, 'a' as b
union select 2, 'b'
returns two rows each with two columns.
Example of a more complex processing before returning a result set:
create procedure p
as
begin
declare #a int, #b varchar(10)
select #a = 1
select #b = convert(varchar(10), #a)
select #a = #a + 1
select #a as a, #b as b -- this will be the resultset returned to the client
end
All you need to do is, just save the data for each row in a temp table or table variable and just write a SELECT statement at the the end of the Stored Procedure.
Your question is not clear what you need exactly, you have a cursor and while loop, they seem to be redundant