issue when executing the pgsql complaining about loop - arrays

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;

Related

Getting error in EXECUTE statement when a null value is encountered

I have created following stored procedure to update recon_dashboard table.
create or replace procedure ca_adhoc_view.sp_count_recon_refresh()
language plpgsql as
$$
declare
f record;
BEGIN
for f in select alvid from ca_adhoc_view.recon_dashboard
loop
raise notice '% alv',f.alv;
execute 'update ca_adhoc_view.recon_dashboard set alv_count =
(select count(*) from ' || f.alv || ')
where id='|| f.id;
end loop;
END;
$$;
It works fine unless there is a null value in the alv, then it throws an error.
I tried to check for null like:
create or replace procedure ca_adhoc_view.sp_count_recon_refresh()
language plpgsql as
$$
declare
f record;
BEGIN
for f in select alv,id from ca_adhoc_view.recon_dashboard
loop
raise notice '% alv',f.alv;
execute 'update ca_adhoc_view.recon_dashboard set alv_count =
(IF '||f.alv||' is not null
then (select count(*) from ' || f.alv || ')
END IF;)
where id='|| f.id;
end loop;
END;
$$;
But it throws error when I try to call the SP.
Error: SQL Error [42601]: ERROR: syntax error at or near "ca_view"
Where: PL/pgSQL function "sp_count_recon_refresh" line 7 at execute statement
In error "ca_view" is a recod. alv column has values like 'ca_view.table1', 'ca_view.table2' and so on.
This probably fixes your problems:
CREATE OR REPLACE PROCEDURE ca_adhoc_view.sp_count_recon_refresh()
LANGUAGE plpgsql AS
$func$
DECLARE
f record;
BEGIN
FOR f IN
SELECT alv, id FROM ca_adhoc_view.recon_dashboard
LOOP
RAISE NOTICE '% alv', f.alv;
IF f.alv IS NOT NULL THEN
EXECUTE format(
'UPDATE ca_adhoc_view.recon_dashboard
SET alv_count = (SELECT count(*) FROM %I)
WHERE id = $1', f.alv)
USING f.id;
END IF;
END LOOP;
END
$func$;
"Problems" (plural). Besides defending against a NULL value for the table name with proper syntax, this also prevents SQL injection. Your original is wide open there. Related:
PostgreSQL - Writing dynamic sql in stored procedure that returns a result set
Table name as a PostgreSQL function parameter
That said, it would be more efficient to run a single UPDATE instead of updating one row per loop iteration. Consider building and executing a single statement.

Snowflake SQL Procedure Parameters

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.

Merging and returning arrays

I've two functions,
CREATE OR REPLACE FUNCTION function_a(input varchar)
RETURNS setof integer AS $$
BEGIN
RETURN QUERY
SELECT somecolumn FROM some_things WHERE a_column = input;
END;
$$ LANGUAGE PLpgSQL;
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
DECLARE
input varchar;
result integer[];
BEGIN
FOREACH input IN ARRAY inputs LOOP
result := result || ARRAY[function_a(input)];
END LOOP;
END;
$$ LANGUAGE PLpgSQL;
I am running it like,
SELECT function_b(ARRAY['a', 'b']);
The error,
ERROR: query "SELECT result || ARRAY[function_a(input)]" returned more than one row
CONTEXT: PL/pgSQL function function_b(character varying[]) line 7 at assignment
All I want to do is to run a function over an array. I've always used scripting languages like Ruby to do this kind of stuff instead of using SQL, but I'm trying to learn SQL as it is much faster to get results on the db console itself. I wish it wasn't so frustrating.
First, in SQL we mainly write queries. You can do the same with a simple query:
select somecolumn
from some_things
where a_column = any(array['a', 'b']);
If you need a function, it may be an SQL one:
create or replace function sql_function(inputs text[])
returns setof integer language sql as $$
select somecolumn
from some_things
where a_column = any(inputs);
$$;
SQL functions are simpler and usually faster than plpgsql ones.
Back to your function_b() - the array constructor should look like this:
ARRAY(SELECT function_a(input))
Note also that the function does not return anything. Because you aggregate results in an array, you should unnest it to return rows:
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
DECLARE
input varchar;
result integer[];
BEGIN
FOREACH input IN ARRAY inputs LOOP
result := result || ARRAY(SELECT function_a(input));
END LOOP;
RETURN QUERY SELECT unnest(result);
END;
$$ LANGUAGE PLpgSQL;
You don't need the first function at all. I believe you want to return a set of integers from the table for the given input. A function like this is all you need.
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
BEGIN
RETURN QUERY SELECT somecolumn
FROM some_things WHERE a_column = ANY ( inputs );
END;
$$ LANGUAGE PLpgSQL;
Demo

Getting multiple values from select statement into a variable of a procedure

I am trying to craete a procedure where the input to the procedure will be a string like 'a,b,c,d' and output will be the values from a table which match the comma separated value from in IN sting.
For that --
create table test (nm varchar2(10));
insert into test values ('a');
insert into test values ('b');
insert into test values ('c');
insert into test values ('d');
select * from test;
NM
------
a
b
c
d
Now i am trying to make a procedure where the IN parameter will be the values of NM column of TEST table in a comma separated string like 'a,b,c,d,x,w' there could be false values also.
So the procedure will return only the values which are matching with NM column of the TEST table for this i have created This procedure ---
create or replace procedure p_test (p_nm IN varchar2 /*, p_out OUT sys_refcursor*/)
is
l_len number;
l_val varchar2(10);
l_val1 varchar2(10);
begin
l_len := length(p_nm);
-- dbms_output.put_line(l_len);
begin
for i in 1..l_len
loop
select REGEXP_SUBSTR (p_nm, '([^,]*)(,|$)',1, i , NULL, 1) into l_val from dual;
-- dbms_output.put_line(l_val);
-- open p_out for
select * into l_val1 from test where nm = l_val;
dbms_output.put_line(l_val1);
exit when l_len is null;
end loop;
exception
when no_data_found then
null;
end;
exception
when others then
dbms_output.put_line('Error reason :'||sqlerrm||' error code :'||sqlcode);
end;
EXECUTE p_test('a,,b,c,d,q,w');
OUTPUT --
a
b
c
d
This procedure is giving me output as i required but i need to get that result into a variable which should be OUT parameter of this procedure as it will be called by the JAVA of our application.
As i have already tried to use the refcursor (please see the commented part) but it is giving me no output while a call it.
Its a call to this procedure when i use the refcursor (by removing comments ).
declare
l_out sys_refcursor;
l_val varchar2(20);
l_str varchar2(20) := 'a,b,c,d';
begin
p_test (l_str, l_out);
loop
fetch l_out into l_val;
dbms_output.put_line(l_val);
dbms_output.put_line('a');
exit when l_out%notfound;
end loop;
end;
So here i got stuck with that how to get the multiple output or am i missing something here and if there is a better approach to this requirement as i have come with this so i am sharing it here.
I am using ---
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
Thanks
1) Create collection in oracle. create type list_of_varchar is table of varchar2(20);
2) Use bulk collect into.
declare
cursor c_obj is select object_name from user_objects where length(object_name) < 20;
arrays list_of_varchar;
begin
open c_obj;
fetch c_obj bulk collect into arrays;
close c_obj;
dbms_output.put_line('Fetched '||arrays.count);
end;
3*) And the same code execute in java.
public static void main(String[] args) throws SQLException, Exception {
Connection con = ConnectionDefinition.getOracleConnection(); //my oracle connection
String str = "declare " +
" cursor c_obj is select object_name from user_objects where length(object_name) < 20;" +
" arrays list_of_varchar;" +
"begin " +
" open c_obj;" +
" fetch c_obj bulk collect into arrays;" +
" close c_obj; " +
" dbms_output.put_line('Fetched '||arrays.count);"+
" ? := arrays;" +
"end;";
CallableStatement cs = con.prepareCall(str);
cs.registerOutParameter(1, OracleTypes.ARRAY, "LIST_OF_VARCHAR");
cs.execute();
String[] strings = (String[])cs.getArray(1).getArray();
for(String tmp : strings) {
System.err.println(tmp);
}
cs.close();
con.close();
}

PostgreSQL: column names into array PL/pgSQL

create or replace function extr( tabname text ) returns text[] as
$$
declare cols text[];
begin
execute 'array(select column_name::text from information_schema.columns where table_name = '||quote_literal(tabname)||');' into cols;
return cols;
end;
$$
language 'plpgsql';
select extr('test');
One supplies a table name and wants back its column-names as an array. The above code gives 'syntax error at or near "array"'. How to fix this?
The query should start with select, not array and it doesn't have to be dynamic SQL.
Try this modified version:
create or replace function extr( tabname text ) returns text[] as
$$
declare cols text[];
begin
select array(select column_name::text from information_schema.columns
where table_name = tabname) into cols;
return cols;
end;
$$
language 'plpgsql';

Resources