PL/pgSQL Loops Indefinitely - postgresql

I want to loop between two dates but my PL/pgSQL code goes into indefinite loop. I think I'm missing something here.
do $$
declare
the_dates date;
begin
select gs from generate_series('2019-11-01'::date, '2012-11-30', '1 day') as gs into the_dates;
loop
raise notice '%', the_dates;
end loop;
end
$$
How should I loop between these 2 dates?

You appear to be confused as to the syntax for loops.
What you have here are two separate things:
A query that select zero rows (because you have your dates backwards) into a date variable.
A loop with no limits that will raise a notice forever.
https://www.postgresql.org/docs/current/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING

You are not looping over the result of the SELECT statement.
If you want to loop over the result of a query you need to use for record in select ...
do $$
declare
l_date_row record;
begin
for l_date_row in select gs
from generate_series('2012-11-30'::date, '2019-11-01'::date, '1 day') as gs
raise notice '%', l_date_row.gs;
end loop;
end
$$

Related

FOR loop over a date range in Postgres

In one of my functions in Postgres, I am trying to loop over a range of dates using the following code:
FOR timesheet_date IN select generate_series('2012-11-24'::date,'2012-12-03','1 day'::interval)::date LOOP
//My code goes here
END LOOP;
But I am getting an error
Now as am getting dates, I think it is not a record variable and hence the error.
But, how can I loop through a date range ? I am very new to Postgres actually.
DO $$
declare
dt record;
begin
FOR dt IN SELECT generate_series('2023-02-10'::date, '2023-02-15'::date, '1 day'::interval) LOOP
RAISE NOTICE 'Processing date: %', dt.generate_series;
END LOOP;
end; $$

for loop not returning any values

do
$$
declare
f record;
begin
for f in SELECT DISTINCT station_code FROM rainfall LIMIT 3
loop
EXECUTE format('SELECT * FROM rainfall WHERE station_code=''%I''', f.station_code);
RAISE NOTICE '%',f.station_code;
end loop;
end;
$$
Hi i'm trying to print out the execute statement for each f.station_code. It doesn't return any output but doesn't throw error. The raise notice however returns
NOTICE: MALUDAM
NOTICE: IGAN
NOTICE: LUBOKANTU
DO
Query returned successfully in 4 secs 4 msec.
Appreciate any help given.
A do block can not return a result.
But I don't see any reason to use PL/pgSQL, a loop or even dynamic SQL for this:
A single query like the following will do what you want:
SELECT *
FROM rainfall
WHERE station_code IN (SELECT station_code FROM rainfall LIMIT 3);
(The IN with a LIMIT on the same table makes zero sense to me though)

How to access a record's field in an Execute sentence?

I need to access a dynamic field from a dynamic table, and I have this code for doing this
Here f_field takes the name of a column from t_table dynamicly.
When t_table is declared as a record this code gets an error. But when declared as a static column fieldType it runs as expected.
The question is, How can I declared t_row for this code to run properly, or how can I achieve the same by other way. Remember that t_table and f_field are dynamic and therefore their values change.
FOR t_row IN EXECUTE 'SELECT * from ' || t_table loop
EXECUTE format('select $1.%I', f_field) USING t_row into f;
RAISE notice '%', f;
END LOOP;
if I get your task right, you dont need $1 and USING here at all, eg:
do
$$
declare
t_table text;
f text;
f_field text := 'oid';
r record;
begin
for r in (select relname from pg_class where relname = 'pg_database') loop
EXECUTE format('select %I from %I', f_field, r.relname) into f;
RAISE notice '%', f;
end loop;
end;
$$
;
mind it raises only first row out of many this way
NOTICE: 12669
DO
When I was doing my research I found out that sentences like EXECUTE... doesn't understand records structure, this was by experimenting with plpgsql because there is not doc about this. So I need to convert t_row in another kind of object that has methods to extract a value from a dynamic field, I chose json because I think it is easy to use. This is my code.
FOR t_row IN EXECUTE 'SELECT * from dd.a' loop
f_json := row_to_json(layer_row);
f := json_extract_path(f_json, f_field);
RAISE notice '%', f;
END LOOP;
It work for fine for me, but this is only a walkaround, if someone could point out why is my first code not working I will apreciate it. Thanks in advance

how to execute pgsql script in pgAdmin?

I want to execute some pgScript directly from the pgAdmin editor UI.
FOR i IN 1..10 LOOP
PRINT i; -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop
END LOOP;
But I always got
[ERROR ] 1.0: syntax error, unexpected character
I also tried to wrap the code with do$$...$$, but does not solve the problem.
apart from Clodoaldo Neto's Answer.You can try this also
DO
$$
BEGIN
FOR i IN 1..10 LOOP
RAISE NOTICE '%', i; -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop
END LOOP;
END
$$
There is no PRINT command. Use raise notice instead.
create function f()
returns void as $$
begin
FOR i IN 1..10 LOOP
raise notice '%', i; -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop
END LOOP;
end;
$$ language plpgsql;
http://www.postgresql.org/docs/current/static/plpgsql.html

Variables for identifiers inside IF EXISTS in a plpgsql function

CREATE OR REPLACE FUNCTION drop_now()
RETURNS void AS
$BODY$
DECLARE
row record;
BEGIN
RAISE INFO 'in';
FOR row IN
select relname from pg_stat_user_tables
WHERE schemaname='public' AND relname LIKE '%test%'
LOOP
IF EXISTS(SELECT row.relname.tm FROM row.relname
WHERE row.relname.tm < current_timestamp - INTERVAL '90 minutes'
LIMIT 1)
THEN
-- EXECUTE 'DROP TABLE ' || quote_ident(row.relname);
RAISE INFO 'Dropped table: %', quote_ident(row.relname);
END IF;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Could you tell me how to use variables in SELECT which is inside IF EXISTS? At the present moment, row.relname.tm and row.relname are treated literally which is not I want.
CREATE OR REPLACE FUNCTION drop_now()
RETURNS void AS
$func$
DECLARE
_tbl regclass;
_found int;
BEGIN
FOR _tbl IN
SELECT relid
FROM pg_stat_user_tables
WHERE schemaname = 'public'
AND relname LIKE '%test%'
LOOP
EXECUTE format($f$SELECT 1 FROM %s
WHERE tm < now() - interval '90 min'$f$, _tbl);
GET DIAGNOSTICS _found = ROW_COUNT;
IF _found > 0 THEN
-- EXECUTE 'DROP TABLE ' || _tbl;
RAISE NOTICE 'Dropped table: %', _tbl;
END IF;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Major points
row is a reserved word in the SQL standard. It's use is allowed in Postgres, but it's still unwise. I make it a habbit to prepend psql variable with an underscore _ to avoid any naming conflicts.
You don't don't select the whole row anyway, just the table name in this example. Best use a variable of type regclass, thereby avoiding SQL injection by way of illegal table names automatically. Details in this related answer:
Table name as a PostgreSQL function parameter
You don't need LIMIT in an EXISTS expression, which only checks for the existence of any rows. And you don't need meaningful target columns for the same reason. Just write SELECT 1 or SELECT * or something.
You need dynamic SQL for queries with variable identifiers. Plain SQL does not allow for that. I.e.: build a query string and EXECUTE it. Details in this closely related answer:
Dynamic SQL (EXECUTE) as condition for IF statement
The same is true for a DROP statement, should you want to run it. I added a comment.
You'll need to build your query as a string then execute that - see the section on executing dynamic commands in the plpgsql section of the manual.