What is equivalent of Firebird SQL 'for do' and 'select into' for postgresql - postgresql

In a Firebird SQL stored procedure I use a 'select into' in a 'for do' loop and I don't find the equivalent for pg function.
for select purchase.quantity, purchase.purchasevalue, purchase.purchased, purchase.id from purchase
join cellarbook cb on purchase.fk_cellarbook_id = cb.id
join bottle bot on cb.fk_bottle_id = bot.id
where bot.id = :bottleid
order by purchase.purchased ASC
into :purquantity, :purvalue, :purdate, :purid
do
begin
/* calculate quantity on hand at point of purchase
here come some more 'select' and calculations and
then and 'update' */
select sum(psum.quantity) as purquantitysum from purchase
join cellarbook cb on psum.fk_cellarbook_id = cb.id
join bottle bot on cb.fk_bottle_id = bot.id
where bot.id = bottleid and psum.purchased <= pur.purchased and psum.id <> pur.id
into :purquantitysum
end
I think it is a 'for in loop' but I am hung up on what the equivalent for the 'select into' is.

You need to use a record variable for this:
declare
r record;
begin
for r in
select col_1, col_2 from some_table;
loop
select sum(x)
from other_table
where id = r.col_1;
end loop;
end;
More examples are in the manual:
http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING
When you run update or select statements inside a loop is usually code-smell ("row-by-row processing"). In most of the cases it is much more efficient to do a bulk processing of everything in a single statement.

I'd use a cursor. There are several variations on the same theme available. I normally use this:
declare mycursor cursor for select a, b from c;
declare d, e bigint;
begin
loop
fetch from mycursor into d, e
exit when not found;
-- do your thing here
end loop;
close mycursor;
-- maybe do some other stuff
end;

Related

Is it worth Parallel/Concurrent INSERT INTO... (SELECT...) to the same Table in Postgres?

I was attempting an INSERT INTO.... ( SELECT... ) (inserting a batch of rows from SELECT... subquery), onto the same table in my database. For the most part it was working, however, I did see a "Deadlock" exception logged every now and then. Does it make sense to do this or is there a way to avoid a deadlock scenario? On a high-level, my queries both resemble this structure:
CREATE OR REPLACE PROCEDURE myConcurrentProc() LANGUAGE plpgsql
AS $procedure$
DECLARE
BEGIN
LOOP
EXIT WHEN row_count = 0
WITH cte AS (SELECT *
FROM TableA tbla
WHERE EXISTS (SELECT 1 FROM TableB tblb WHERE tblb.id = tbla.id)
INSERT INTO concurrent_table (SELECT id FROM cte);
COMMIT;
UPDATE log_tbl
SET status = 'FINISHED',
WHERE job_name = 'tblA_and_B_job';
END LOOP;
END
$procedure$;
And the other script that runs in parallel and INSERTS... also to the same table is also basically:
CREATE OR REPLACE PROCEDURE myConcurrentProc() LANGUAGE plpgsql
AS $procedure$
DECLARE
BEGIN
LOOP
EXIT WHEN row_count = 0
WITH cte AS (SELECT *
FROM TableC c
WHERE EXISTS (SELECT 1 FROM TableD d WHERE d.id = tblc.id)
INSERT INTO concurrent_table (SELECT id FROM cte);
COMMIT;
UPDATE log_tbl
SET status = 'FINISHED',
WHERE job_name = 'tbl_C_and_D_job';
END LOOP;
END
$procedure$;
So you can see I'm querying two different tables in each script, however inserting into the same some_table. I also have the UPDATE... statement that writes to a log table so I suppose that could also cause issues. Is there any way to use BEGIN... END here and COMMIT to avoid any deadlock/concurrency issues or should I just create a 2nd table to hold the "tbl_C_and_D_job" data?

Syntax error when calling one function from another

I am trying to create a function which calls 2 other functions.
Below is the calling function's code from where I am trying to call 2 another functions, schema1.func1() and schema1.func2().
But it is throwing error at the line SELECT schema1.func1(temp_val); saying:
syntax error at or near "SELECT".
I tried to figure out the correct syntax but couldn't resolve.
I am using Postgres version 1.14.3
DECLARE
temp_val int;
cursor1 CURSOR
FOR
SELECT col1 from schema1.table1;
BEGIN
OPEN cursor1;
LOOP
FETCH cursor1 INTO temp_val;
EXIT WHEN NOT FOUND;
SELECT CASE
WHEN NOT EXISTS (SELECT col2 FROM schema1.table2 WHERE col2 = temp_val)
THEN
BEGIN
SELECT schema1.func1(temp_val);
SELECT schema1.func2(temp_val);
END;
END CASE;
END LOOP;
CLOSE cursor1;
END;
There is a ; missing at:
SELECT CASE WHEN NOT EXISTS (SELECT col2 FROM schema1.table2 WHERE col2 = temp_val)
so..
SELECT CASE WHEN NOT EXISTS (SELECT col2 FROM schema1.table2 WHERE col2 = temp_val);
You can't mix PL/pgSQL BEGIN ... END blocks with SQL statement - even if that SQL statement is part of a PL/pgSQL function. So the BEGIN inside the THEN part of the CASE expression is invalid. A SQL CASE expression ends with just an END. A PL/pgSQL CASE statement would end with END CASE. As you are trying to use a CASE expression, it would require just END, not end case.
You also need to use perform in order to call a function where you want to discard the result.
It's not clear to me what exactly you want to achieve. If you only want to call those two functions if a row does not exist in table2 then you can do this with a single loop:
DECLARE
t1_rec record;
BEGIN
FOR t1_rec IN select t1.col1
from table1 t1
where not exists (select *
from table2 t2
where t2.col2 = t1.col1)
LOOP
perform schema1.func1(t1_rec.col1);
perform schema1.func2(t1_rec.col1);
END LOOP;
END;

PL/SQL SELECT INTO alternative

I have few SELECT statements that are made by joining multiple tables.
Each select is returning single row but from 10 to 20 fields in that row. What is the easiest way to store that data for later use?
I would like to avoid creating 50 variables and writing select into statements, using cursors and loop for single row is not the smartest idea from what i read around.
Is there a good way to do this?
Stupid example so you can get general idea
SELECT t1.field1
, t1.field2
, t1.field3
, t1.field4
, t2.field5
, t2.field6
, t2.field7
, t3.field8
FROM table1 t1
JOIN table2 t2 ON something
JOIN table3 t3 ON something
Sorry for errors in my english and thanks in advance
Create a view from your select statement. Thereafter can reference a record by using a single variable of type <viewname>%ROWTYPE.
Another option would be to wrap the select in an implicit cursor loop:
DECLARE
strvar VARCHAR2(400); -- demo purpose only
BEGIN
-- ...
FOR i IN (
-- ... here goesyour select statement ...
) LOOP
strvar := i.field1 || i.field2; -- ... whatever
END LOOP;
-- ...
END;
-- ...
Still another option is the declaration of a record type and a record variable:
DECLARE
TYPE tRec IS RECORD (
field1 table1.field1%TYPE
, field2 table1.field2%TYPE
, field3 table1.field3%TYPE
, field4 table1.field4%TYPE
, field5 table2.field5%TYPE
, field6 table2.field6%TYPE
, field7 table2.field7%TYPE
, field8 table3.field8%TYPE
)
r tRec;
BEGIN
-- ...
SELECT --...
INTO r
FROM --...
;
-- ...
END;
-- ...
Every time you make a select you will have a cursor - there's no escape from that.
Views and explicit cursors are a way to reuse select statements. Which one is better option depends on the case.
rowtype-attribute is handy way to create records automatically based on tables/views/cursors. I think this is the closest to your requirement what PL/SQL can offer.
create or replace package so51 is
cursor cursor1 is -- an example single row query
select
dual.* -- all table columns included
,'Y' as str
,rownum as num
,sysdate as date_
from dual
;
function get_data return cursor1%rowtype;
end;
/
show errors
create or replace package body so51 is
-- function never changes when cursor1 changes
function get_data return cursor1%rowtype is
v_data cursor1%rowtype;
begin
open cursor1;
fetch cursor1 into v_data;
close cursor1;
return v_data;
end;
end;
/
show errors
declare
v_data constant so51.cursor1%rowtype := so51.get_data;
begin
-- use only the data you need
dbms_output.put_line('v_data.dummy = ' || v_data.dummy);
dbms_output.put_line('v_data.str = ' || v_data.str);
dbms_output.put_line('v_data.num = ' || v_data.num);
dbms_output.put_line('v_data.date_ = ' || v_data.date_);
end;
/
Example run
SQL> #so51.sql
Package created.
No errors.
Package body created.
No errors.
v_data.dummy = X
v_data.str = Y
v_data.num = 1
v_data.date_ = 2015-11-23 09:42:02
PL/SQL procedure successfully completed.
SQL>

PostgreSQl function return multiple dynamic result sets

I have an old MSSQL procedure that needs to be ported to a PostgreSQL function. Basically the SQL procedure consist in a CURSOR over a select statement. For each cursor entity i have three select statements based on the current cursor output.
FETCH NEXT FROM #cursor INTO #entityId
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT * FROM table1 WHERE col1 = #entityId
SELECT * FROM table2 WHERE col2 = #entityId
SELECT * FROM table3 WHERE col3 = #entityId
END
The tables from the SELECT statements have different columns.
I know that the PostgreSQL use refcursor in order to return multiple result sets but the question is if is possible to open and return multiple dynamic refcursors inside of a loop?
The Npgsql .NET data provider is used for handling the results.
Postgres test code with only 1 cursor inside loop:
CREATE OR REPLACE FUNCTION "TestCursor"(refcursor)
RETURNS SETOF refcursor AS
$BODY$
DECLARE
entity_id integer;
BEGIN
FOR entity_id IN SELECT "FolderID" from "Folder"
LOOP
OPEN $1 FOR SELECT * FROM "FolderInfo" WHERE "FolderID" = entity_id;
RETURN NEXT $1;
CLOSE $1;
END LOOP;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
Then the test code:
BEGIN;
SELECT * FROM "TestCursor"('c');
FETCH ALL IN c;
COMMIT;
The SELECT * FROM "TestCursor"('c'); output is like on screenshot:
Then when i try to fetch data i get the error: ERROR: cursor "c" does not exist
You can emulate it via SETOF refcursor. But it is not good idea. This T-SQL pattern is not supported well in Postgres, and should be prohibited when it is possible. PostgreSQL support functions - function can return scalar, vector or relation. That is all. Usually in 90% is possible to rewrite T-SQL procedures to clean PostgreSQL functions.

EXECUTE...USING statement in PL/pgSQL doesn't work with record type?

I'm trying to write a function in PL/PgSQL that have to work with a table it receives as a parameter.
I use EXECUTE..INTO..USING statements within the function definition to build dynamic queries (it's the only way I know to do this) but ... I encountered a problem with RECORD data types.
Let's consider the follow (extremely simplified) example.
-- A table with some values.
DROP TABLE IF EXISTS table1;
CREATE TABLE table1 (
code INT,
descr TEXT
);
INSERT INTO table1 VALUES ('1','a');
INSERT INTO table1 VALUES ('2','b');
-- The function code.
DROP FUNCTION IF EXISTS foo (TEXT);
CREATE FUNCTION foo (tbl_name TEXT) RETURNS VOID AS $$
DECLARE
r RECORD;
d TEXT;
BEGIN
FOR r IN
EXECUTE 'SELECT * FROM ' || tbl_name
LOOP
--SELECT r.descr INTO d; --IT WORK
EXECUTE 'SELECT ($1)' || '.descr' INTO d USING r; --IT DOES NOT WORK
RAISE NOTICE '%', d;
END LOOP;
END;
$$ LANGUAGE plpgsql STRICT;
-- Call foo function on table1
SELECT foo('table1');
It output the following error:
ERROR: could not identify column "descr" in record data type
although the syntax I used seems valid to me. I can't use the static select (commented in the example) because I want to dinamically refer the columns names.
So..someone know what's wrong with the above code?
It's true. You cannot to use type record outside PL/pgSQL space.
RECORD value is valid only in plpgsql.
you can do
EXECUTE 'SELECT $1.descr' INTO d USING r::text::xx;
$1 should be inside the || ,like || $1 || and give spaces properly then it will work.
BEGIN
EXECUTE ' delete from ' || quote_ident($1) || ' where condition ';
END;