How to use a cursor inside a loop in a PostgreSQL script - postgresql

I want to make a script for a PostgreSQL database which reads a table and prints its content. I have used the following code
declare myCursor cursor for
select col1, col2 from tab1;
begin
loop
DBMS_OUTPUT.PUT_LINE(myCursor .col1+" "+myCursor.col2);
end loop;
end;
But it doesn't work.

Eventually I have found out:
do $$
declare myCursor cursor for
select col1, col2 from tab1;
begin
for myIterator in myCursor loop
raise notice '% %', myIterator.col1, myIterator.col2;
end loop;
end $$;

Related

Postgres Update Statement: how to set a column's value only if the column name contains a certain substring?

Each of the following tables ['table1', 'table2'] are part of the public schema, knowing that each table may contain multiple columns containing the substring 'substring' in the name for Example let's look at the following :
Table_1 (xyz, xyz_substring,...some_other_columns... abc,
abc_substring)
Table_2 (xyz, xyz_substring,..some_other_columns... abc,
abc_substring)
I am coming to this from a pythonic way of thinking, but basically, how could one execute a statement without knowing exactly what to set since the columns we need to target need to meet a certain criteria ?
I had the idea to add a another loop over the column names of the current table and check if the name meets the criteria. then execute the query but It feels very far away from optimal.
DO $$
declare
t text;
tablenames TEXT ARRAY DEFAULT ARRAY['table_1', 'table_2'];
BEGIN
FOREACH t IN ARRAY tablenames
LOOP
raise notice 'table(%)', t;
-- update : for all the column that contain 'substring' in their names set a value
END LOOP;
END$$;
EDIT:
Thanks for the answer #Stefanov.sm , i followed exactly your thought and was able to base your logic to have just one statement :
DO $$
declare
t text;
tablenames TEXT ARRAY DEFAULT ARRAY['table_1', 'table_2'];
dynsql text;
colname text;
BEGIN
FOREACH t IN ARRAY tablenames LOOP
raise notice 'table (%)', t;
dynsql := format('update %I set', t);
for colname in select column_name from information_schema.columns where table_schema = 'public' and table_name = t
loop
if colname like '%substring%' then
dynsql := concat(dynsql,format(' %I = ....whatever expression here (Make sure to check if you should use Literal formatter %L if needed) ....,',colname,...whateverargs...));
end if;
end loop;
dynsql := concat(dynsql,';'); -- not sure if required.
raise notice 'SQL to execute (%)', dynsql;
execute dynsql;
END LOOP;
END;
$$;
Extract the list of columns of each table and then format/execute dynamic SQL. Something like
DO $$
declare
t text;
tablenames text[] DEFAULT ARRAY['table_1', 'table_2'];
dynsql text;
colname text;
BEGIN
FOREACH t IN ARRAY tablenames LOOP
raise notice 'table (%)', t;
for colname in select column_name
from information_schema.columns
where table_schema = 'public' and table_name = t loop
if colname ~ '__substring$' then
dynsql := format('update %I set %I = ...expression... ...other clauses if any...', t, colname);
raise notice 'SQL to execute (%)', dynsql;
execute dynsql;
end if;
end loop;
END LOOP;
END;
$$;
This will cause excessive bloat so do not forget to vacuum your tables. If your tables' schema is not public then edit the select from information_schema accordingly. You may use pg_catalog resource instead of information_schema too.

What is the equivalent of PL/SQL %ISOPEN in PL/pgSQL?

I'm migrating an Oracle PLSQL SP to be compatible with Postgres plpgsql (version PostgreSQL 13.6 on x86_64-pc-linux-gnu, compiled by x86_64-pc-linux-gnu-gcc (GCC) 7.4.0, 64-bit).
The exception block of the PLSQL SP has the below code:
exception
when others then
if CURR1%isopen then
close SPV_RECON_INFO;
end if;
open CURR1 for execute select sysdate from dual;
END;
How can %isopen be implemented in Postgres?
That is simple. You have to assign a name to the cursor variable, then you can search for that cursor in pg_cursors. If there is a row with that name, the cursor is open.
Here is a self-contained example:
DO
$$DECLARE
c refcursor;
BEGIN
c := 'mycursor';
/* cursor is not open, EXIST returns FALSE */
RAISE NOTICE '%', EXISTS (SELECT 1 FROM pg_cursors WHERE name = 'mycursor');
OPEN c FOR SELECT * FROM pg_class;
/* cursor is open, EXIST returns TRUE */
RAISE NOTICE '%', EXISTS (SELECT 1 FROM pg_cursors WHERE name = 'mycursor');
END;$$;
NOTICE: f
NOTICE: t
If you do not assign a name, PostgreSQL will generate a name (but you don't know what the name is).
PostgreSQL cursors do not support %ISOPEN or %NOTFOUND. To address this problem %ISOPEN can be replaced by a boolean variable declared internally in the procedure and is updated manually when the cursor is opened or closed.
http://wiki.openbravo.com/wiki/PL-SQL_code_rules_to_write_Oracle_and_Postgresql_code
I often found it convenient in cases like this to create a function that emulates Oracle. In his case something like:
create or replace function cursor_isopen(cur text)
returns boolean
language sql
as $$
select exists (select null
from pg_cursors
where name = cur
) ;
$$;
Then your code becomes something like:
exception
when others then
if cursor_isopen(cur_name::text) then
close SPV_RECON_INFO;
end if;
Of course you need to have preset the cursor name as Laurenz Albe has pointed out. Sample test case.
do $$
declare
cur1 cursor for select table_name from information_schema.tables;
cur2 cursor for select table_name from information_schema.tables;
table_name text;
begin
cur1 := 'closed-cursor';
cur2 := 'open-cursor';
open cur2;
if cursor_isopen(cur1::text)
then
fetch cur1 into table_name;
raise notice 'First table name: %', table_name;
close cur1;
else raise notice 'cursor_isopen(''%'') returned %', cur1::text, cursor_isopen(cur1::text);
end if;
if cursor_isopen(cur2::text)
then
fetch cur2 into table_name;
raise notice 'First table name: %', table_name;
close cur2;
else raise notice 'cursor_isopen(''%'') returned %', cur1::text, cursor_isopen(cur1::text);
end if;
end;
$$;
results:
cursor_isopen('closed-cursor') returned f
cursor_isopen('open-cursor') returned t. First table name: task_states

Postgresql - psql - for loop not displaying output

The following sql file runs without error; however it does no display any output.
The file is executed with: pslq -d database -f filename.
How can this be modified to display output?
do $$
declare tn varchar ;
begin
for tn in
select tablename from pg_tables where tableowner not like 'postgres%'
loop
EXECUTE format('SELECT count(*) from %I;', tn);
end loop;
end;
$$;
The problem is that the result of a dynamic query is discarded unless you use SELECT ... INTO:
DO $$
DECLARE
tn text;
total bigint;
BEGIN
FOR tn IN
SELECT tablename FROM pg_tables WHERE tableowner NOT LIKE 'postgres%'
LOOP
EXECUTE format('SELECT count(*) from %I;', tn) INTO total;
RAISE NOTICE 'Table: % rows: %', tn, total;
END LOOP;
END;
$$;

How to loop inside cursor in PostgreSQL

I have a function
drop function ProcessReward();
CREATE OR REPLACE FUNCTION ProcessReward()
RETURNS text AS $$
DECLARE
sessionid NO SCROLL CURSOR FOR SELECT pg."Setting",pg."UserId",pg."Id" FROM "Development"."PersonGame" pg inner join "Development"."Game" g on g."Id" = pg."GameId" pg."GameId"=1 for read only;
titles TEXT DEFAULT '';
rec record;
jsonrec record;
jsonrecord record;
BEGIN
OPEN sessionid;
loop
FETCH sessionid INTO rec;
if not found then
exit ;
end if;
EXECUTE 'select * from "Development"."GameRecipient" where "PersonGameId"=$1' into jsonrecord using rec."Id";
--I want to loop here every row returned by above query
--loop start
-- do your task
--loop end
end loop;
return titles;
END;
$$ LANGUAGE plpgsql;
My query
EXECUTE 'select * from "Development"."GameRecipient" where
"PersonGameId"=$1' into jsonrecord using rec."Id";
returns
col1 col2 col3
123 324 444
345 222 765
I want to process all rows returned by above query,How to achieve this in PostgreSQL.

Syntax error while creating function in postgresql

I got a syntax error while creating a procedure in postgresql.Here I attached my code.I got a error syntax error near "Continue"
create function patient_form_values() RETURNS void AS
$$ begin
DECLARE columnName varchar(200) ;
DECLARE done boolean default true;
DECLARE CONTINUE handler for not found set done = false;
DECLARE cur1 cursor for select distinct COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'currentdiagnosis';
open cur1;
read_loop : loop
fetch from cur1 into columnName;
if done then leave read_loop;
end if;
set #insertValues := concat('INSERT INTO patient_form_temp(patient_id, form_template_id, creator_id, created_date)
SELECT c.patient_id as patient_id, 41 AS form_template_id, 2 AS creator_id, c.created_date AS created_date
FROM currentdiagnosis c
WHERE c.', columnName,' IS NOT NULL GROUP BY c.patient_id, c.created_date');
select #insertValues;
prepare stmt from #insertValues;
execute stmt;
end loop;
close cur1;
end ;
$$ LANGUAGE plpgsql
You are trying to use a MySQL (or other DB?) function in PostgreSQL. There is no concept of CONTINUE HANDLER in PostgreSQL, so you have to convert the function into PostgreSQL format.
drop FUNCTION if exists migratePartnerAdvertiser();
CREATE OR REPLACE FUNCTION migratePartnerAdvertiser() RETURNS int4 AS '
DECLARE r RECORD;
BEGIN
FOR r IN select distinct COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = ''currentdiagnosis'' and table_schema=''public'' LOOP
EXECUTE concat(''INSERT INTO patient_form_temp(patient_id, form_template_id, creator_id, created_date) SELECT c.patient_id as patient_id, 41 AS form_template_id, 2 AS creator_id, c.reg_date AS created_date FROM currentdiagnosis c WHERE c.'' , r.column_name , '' IS NOT NULL GROUP BY c.patient_id, c.reg_date'');
END LOOP;
return 1;
END;
' LANGUAGE plpgsql;