I encountered strange behaviour in PL/pgSQL for PostgreSQL. I am making a function that has two separate FOR loops. The first one runs perfectly but the second seems to be forgotten. This is how the code looks roughly:
Declare r RECORD;
BEGIN
FOR r in (select something from a)
LOOP
DO SOMETHING;
END LOOP;
r:=NULL;
FOR r in (select something from b)
LOOP
DO SOMETHING;
END LOOP;
END;
When put in separate functions both FOR loops work well. I also tried to use 2 separate record variables but that didn't work either.
I can't understand why the second loop is ignored.
Thanks!
Related
My query doesnt even work, but hopefully the logic comes through. Basically Im using datavault.dimdates_csv to produce a row for each date/day. Then for each day Im trying to get all account ids and for each and call a function using the date and for the account.
is there a better approach to getting my data? I know nested loops arnt that great for sql.
do
$$
declare
date_record record;
account record;
begin
for date_record in select d."date" from datavault.dimdates_csv d
for account in select sad.id from datavault.sat_account_details sad
select datavault.account_active_for_date(date_record , account)
loop
loop
end loop;
end;
$$
It's hard to follow your business logic but syntax-wise your block needs correction. Please note that d."date" and sad.id are scalars (I assume a date and an integer) and not records.
do
$$
declare
running_date date;
running_id integer;
begin
for running_date in select d."date" from datavault.dimdates_csv d loop
for running_id in select sad.id from datavault.sat_account_details sad loop
perform datavault.account_active_for_date(running_date, running_id);
end loop;
end loop;
end;
$$;
As far as I can see you are calling function datavault.account_active_for_date for every pair of d."date" and sad.id. If this is true then you can simply
select datavault.account_active_for_date(d."date", sad.id)
from datavault.dimdates_csv d, datavault.sat_account_details sad;
and ignore the resultset.
inside an anonymous block, I have nested a declare...begin...end. Inner block cannot "see" a temp table created in outer block. What is best way to solve, still maintaining creating of temp table inside?
The below FAILs with: CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 152
do
$$
declare
xyz...
being
create temp table x ... <depending on xyz>
...
declare
r x%rowtype -> FAIL
begin
...
end;
end;
$$
It cannot to work. This error is not related to block scopes, but it is related to timing. The temp table is created in runtime, but the x%rowtype is evaluated in validation time (before runtime). There is very simple and robust solution in Postgres. Use record type:
declare r record;
begin
The variable of record type takes real composite type in assign time.
Although PL/pgSQL is similar to PL/SQL, inside it is absolutely different technology, and not all patterns from Oracle are possible or are effective. And sometimes there are different (and sometimes much more comfortable) ways.
I just saw a simple example in another StackOverflow question that used a cursor to loop through a table. I would have just looped through the results of a select query instead of wrapping the select query in a cursor. What is the advantage of using a cursor?
(I couldn't include the example here because StackOverflow thought my question was mostly code, and demanded more details. I've run into that annoying restriction before. If I can ask my question clearly in just a few words, I should be able to. I'll see if I can find a link to that question, and if I can, I'll add the link here.)
Here is the original question where I saw CURSOR used.
What is the advantage of using a cursor?
The only advantage is that you have to write more code (if they pay you for each line of code).
do $$
declare
rec record;
cur cursor for select i from generate_series(1, 3) i;
begin
open cur;
loop
fetch cur into rec;
exit when rec is null;
raise notice '%', rec.i;
end loop;
close cur;
end
$$;
A loop through query results just opens a (virtual) cursor, fetches rows, checks range, exits when needed and closes the cursor for you.
do $$
declare
rec record;
begin
for rec in select i from generate_series(1, 3) i
loop
raise notice '%', rec.i;
end loop;
end
$$;
There are several ways:
Use an explicit cursor in PL/pgSQL and loop through it and process each result row.
Example:
OPEN c FOR SELECT id FROM a WHERE ok;
LOOP
UPDATE b SET a_ok = TRUE WHERE a_id = c.id;
END LOOP;
Use a FOR r IN SELECT ... LOOP in PL/pgSQL. This is effectively the same as 1. with a clearer syntax.
Example:
FOR c IN SELECT id FROM a WHERE ok LOOP
UPDATE b SET a_ok = TRUE WHERE a_id = c.id;
END LOOP;
Run a SELECT query without a cursor and process each result row on the client side, probably issuing a database query for each result.
Example (in pseudocode):
resultset := db_exec('SELECT id FROM a WHERE ok');
while (resultset.next()) {
db_exec('UPDATE b SET a_ok = TRUE WHERE a_id = ' || resultset.get('id'));
}
Use a JOIN.
Example:
UPDATE b SET a_ok = TRUE
FROM a
WHERE a.id = b.a_id AND a.ok;
Method 3. is the most terrible way conceivable to solve the problem, because it causes a lot of client-server round trips and has the database parse a gazillion statements.
Alas, it is often the way how SQL novices attack the problem. I call it home-grown nested loop join.
On top of all that, the client software will often snarf the complete result set from the first query into memory, which causes yet another problem.
Methods 1. and 2. are equivalent, except that 2. is more elegant. It saves the round trips and uses prepared statements under the hood, so the UPDATE doesn't have to be parsed all the time. Still, the executor has to run many times, and PL/pgSQL is known not to be particularly fast. It is also a kind of home-grown nested loop join.
Method 4 is the way to go. Not only is everything run in a single query, but PostgreSQL can also use a more effective join strategy if that is better.
I've prepared two execute statements in PostgreSQL which are both update statements. I'd like to run two of them as an iterative loop e.g.
LOOP
EXECUTE stage1
EXECUTE stage2
IF a = b then EXIT;
END IF
END LOOP
But PostgreSQL doesn't like the syntax. Both prepared statements work fine when run on their own, but they take a bit of time and I need to iterate them many times, so I can't really do this manually. Is there some other way of approaching this?
Simple sql language does not support such constructs. If you whant to write in PLPGSQL syntax you have to write a function to do that:
CREATE FUNCTION some_function() RETURNS void AS $$
--your variable declarations here
BEGIN
--your code here
END;
$$ LANGUAGE plpgsql;
you can replace RETURNS void with a type you want to return;
read the documentation here
How can I write in postgresql a function which for each row in a table takes the parameter in "agg_id" column and do some calculations?
In:
FOR r IN SELECT * FROM foo
what the r is?
Thanks
Wild guess from a rather unclear question:
DECLARE
r record;
BEGIN
FOR r IN SELECT * FROM foo
LOOP
RAISE NOTICE 'agg_id is %', r.agg_id;
END LOOP;
END;
See the PL/PgSQL manual.
Note that it's almost always better to use plain SQL, but it's hard for me to make useful suggestions about how with the total lack of information in the question.