stored function in postgres causing the time out - postgresql

I have written a stored function in postgres. When I am running it, it is running for forever.
I am not sure why it is taking too much time even with 1 day data. Idk what is causing the issue.
Could have any one have a look and provide some feedback what it the issue here.
CREATE FUNCTION schema.delete_allHistoryRecord()
RETURNS text
LANGUAGE 'plpgsql'
VOLATILE
AS $BODY$
DECLARE
-- get the 15 old days transaction_history
transaction_object_cursor CURSOR FOR SELECT * from
(SELECT * FROM schema.transaction
WHERE start_time < (NOW() - INTERVAL '15 days')
UNION SELECT * FROM schema.transaction_history
WHERE start_time < (NOW() - INTERVAL '15 days')) trans
join schema.transaction_object_history hist
on hist.transaction_row_id = trans.row_id limit 500;
transaction_older_than_15_days schema.transaction%ROWTYPE;
-- get the 90 old days project_history
project_history_cursor CURSOR FOR SELECT * FROM schema.project_history
WHERE created_at < (now() - INTERVAL '90 Days');
project_history_older_than_90_days schema.project_history%ROWTYPE;
BEGIN
OPEN transaction_object_cursor;
LOOP
FETCH transaction_object_cursor INTO transaction_older_than_15_days;
EXIT WHEN NOT FOUND;
DELETE FROM schema.transaction_object_history
WHERE transaction_row_id = transaction_older_than_15_days.row_id;
END LOOP;
CLOSE transaction_object_cursor;
OPEN project_history_cursor;
LOOP
FETCH project_history_cursor INTO project_history_older_than_90_days;
EXIT WHEN NOT FOUND;
-- delete old data from project_user_history
DELETE FROM schema.project_user_history
WHERE project_id = project_history_older_than_90_days.project_id;
DELETE FROM schema.project_object_history
WHERE project_row_id = project_history_older_than_90_days.row_id;
DELETE FROM schema.transaction_history
WHERE project_id = project_history_older_than_90_days.project_id;
-- delete old data from project_history
DELETE FROM schema.project_history
WHERE project_id = project_history_older_than_90_days.project_id;
END LOOP;
CLOSE project_history_cursor;
RETURN 'Done';
END;
$BODY$;
Query returns the result very fast and also
SELECT * from
(SELECT * FROM schema.transaction
WHERE start_time < (NOW() - INTERVAL '15 days')
UNION SELECT * FROM schema.transaction_history
WHERE start_time < (NOW() - INTERVAL '15 days')) trans
join schema.transaction_object_history hist
on hist.transaction_row_id = trans.row_id limit 500;
and also
OPEN transaction_object_cursor;
LOOP
FETCH transaction_object_cursor INTO transaction_older_than_15_days;
EXIT WHEN NOT FOUND;
cnt:=cnt+1;
transaction_older_than_15_days.row_id;
END LOOP;
CLOSE transaction_object_cursor;
the count is returning 500, it means cursor is fetching the data. still not able to understand why it is looping forever while deleting the data.

Related

Why am I getting a "More than one row returned" error inside this postgres function?

I have a game table and a child gameeffect table. I'm trying to create a function that will find all games for which the most recent gameeffect row has a created_at date older than a certain number of minutes (passed in as a parameter). Here's the code:
create or replace function end_idle_games(idle_time int)
returns table(game_id integer)
language plpgsql
as $$
begin
create temp table temp_game_ids(game_id int);
with open_game_effects as ( -- gets all the game effects for any open games
select ge.*
from gameeffect ge
join game g on g.game_id = ge.game_id
join gamestatus gs on gs.game_status_id = g.game_status_id
where gs.game_status = 'open'
),
latest_game_effects as ( -- gets the latest game effect for each game and calculates the time since it was created in minutes
select oge.*, extract(epoch from (now() at time zone 'utc' - oge.created_at))/60 as idle_minutes
from open_game_effects oge
where oge.game_effect_id = (select max(oge1.game_effect_id) from open_game_effects oge1 group by oge1.game_id)
),
idle_games as ( -- gets all game ids with an idle minutes greater than the passed in idle time
select lge.game_id
from latest_game_effects lge
where lge.idle_minutes > idle_time
)
insert into temp_game_ids (game_id) select i.game_id from idle_games i;
return query
select t.game_id from temp_game_ids t;
end;
$$;
When I call the function e.g. select * from end_idle_games(120); I'm getting a more than one row returned by a subquery used as an expression error. I know that the line it's complaining about is:
insert into temp_game_ids (game_id) select i.game_id from idle_games i
because when I replace it with:
insert into temp_game_ids (game_id) select 99
the function works. What I don't understand is why it's throwing the error?
Stupid error on my part. The error was actually occurring on this line:
where oge.game_effect_id = (select max(oge1.game_effect_id) from open_game_effects oge1 group by oge1.game_id) because the subquery does return multiple rows. I should have been using the 'in' operator instead of '='. Also, #a_horse_with_no_name helped me to see that I could ditch the temp table by wrapping the entire expression in the return statement like so:
create or replace function end_idle_games(idle_time int)
returns table(game_id integer)
language plpgsql
as $$
begin
return query
with open_game_effects as ( -- gets all the game effects for any open games
select ge.*
from gameeffect ge
join game g on g.game_id = ge.game_id
join gamestatus gs on gs.game_status_id = g.game_status_id
where gs.game_status = 'open'
),
latest_game_effects as ( -- gets the latest game effect for each game and calculates the time since it was created in minutes
select oge.*, extract(epoch from (now() at time zone 'utc' - oge.created_at))/60 as idle_minutes
from open_game_effects oge
where oge.game_effect_id in (select max(oge1.game_effect_id) from open_game_effects oge1 group by oge1.game_id)
),
idle_games as ( -- gets all game ids with an idle minutes greater than the passed in idle time
select lge.game_id
from latest_game_effects lge
where lge.idle_minutes > idle_time
)
select i.game_id from idle_games i;
end;
$$;

How can i create n columns in postgresql?

I am trying to write a function that returns random start time (it must be between now and a week long) and an endtime. I wrote this function:
CREATE OR REPLACE FUNCTION random_start_end_time(n integer)
RETURNS TABLE (startime TIMESTAMP WITH TIME ZONE, endtime TIMESTAMP WITH TIME ZONE)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
RETURN QUERY
SELECT NOW() + (random() * (NOW()+'7 days' - NOW())) as startime;
SELECT NOW() + (random() * (NOW()+'7 days' - NOW())) as endtime;
END;
$BODY$
I can't find out how to generate multiple columns. For example I want n=100 columns of random start time and end time to be generated.
In general I can't understand how I can fill an empty table (with this function I am going to fill a table later).
Any thoughts would be valuable.
Thank you.
Use RETURN NEXT to add a row to the result set of a table function and RETURN to end the function execution. You also have to decide if you want a function that returns two columns or two rows. Your case looks like you want to do something like:
CREATE OR REPLACE FUNCTION random_start_end_time(
OUT starttime TIMESTAMP WITH TIME ZONE,
OUT endtime TIMESTAMP WITH TIME ZONE
) RETURNS record
LANGUAGE sql AS
$BODY$
WITH start AS (
SELECT current_timestamp + random() * INTERVAL '7 days' as starttime
)
SELECT starttime,
starttime + random() * INTERVAL '7 days' as endtime
FROM start;
$BODY$;
Call it like
SELECT * FROM random_start_end_time();
If you really want to return several rows, that would be
CREATE OR REPLACE FUNCTION random_start_end_time()
RETURNS SETOF timestamp with time zone
LANGUAGE plpgsql AS
$BODY$
BEGIN
RETURN NEXT current_timestamp + random() * INTERVAL '7 days';
RETURN NEXT current_timestamp + random() * INTERVAL '7 days';
RETURN; /* end the function */
END;
$BODY$;

Commit changes after each iteration inside a function postgres

I am trying to commit the changes after each iteration inside this function to be able to save my progress in case of stopping the function in the middle of work. what should I do?
CREATE OR REPLACE FUNCTION delete_in_loop()
RETURNS INTEGER AS $$
DECLARE
counter INTEGER = 0 ;
i INTEGER = 0 ;
BEGIN
i = (select COUNT("ID") from "AwsSesNotification"
where "UTADateCreatedOn" < (now() - interval '3 month'))/1000 + 1 ;
LOOP
EXIT WHEN counter > i ;
counter = counter+1;
delete from "AwsSesNotification"
where "ID" in(
select "ID" from "AwsSesNotification"
where "UTADateCreatedOn" < (now() - interval '3 month')
limit 1000
);
RAISE NOTICE 'Counter: %', counter;
RAISE NOTICE 'From: %', i;
PERFORM pg_sleep(2);
END LOOP;
return counter;
END;
$$ LANGUAGE plpgsql;
This is known as AUTONOMOUS TRANSACTIONS and is unfortunately not supported in PostgreSQL.
The only way I know to achieve similar effects is by using dblink(), essentially writing to another database which has a separate transaction context. Much slower than normal writing.
Best regards,
Bjarni

POSTGRES UPDATE ON CONFLICT. Error cannot affect a row a second time (dupes) ONLY OCCURS IN FUNCTION. Not as query

I have a query in a function that does not seem to be returning any duplicates from my checks and if ran as a separate query... it works! If ran within a stored function, it gives the error ON CONFLICT DO UPDATE command cannot affect row a second time.
This makes no sense.
CREATE OR REPLACE FUNCTION rollups.compute_daily_rollups_every_hour(
start_time timestamp without time zone,
end_time timestamp without time zone)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
BEGIN
RAISE NOTICE 'Computing daily rollups from % to % (excluded)', start_time, end_time;
RAISE NOTICE 'Aggregating data into daily_rollup';
EXECUTE $$
INSERT INTO rollups.daily_rollup
SELECT
COALESCE(visitors, 0) AS visitors, COALESCE(total_dwell_time, 0) AS total_dwell_time, d.datestamp::date, doorway_id, customer_id, centre_id, postcode, gender, age_group, house_income, no_children, no_cars, shopping_frequency, marital_status, employment_status
FROM (
Select date_trunc('day', (current_date - offs)) AS datestamp
FROM generate_series(0, 365, 1) AS offs
) AS D
LEFT OUTER JOIN (
SELECT cv.datestamp,
round((date_part('epoch'::text, sum(cv.dwell_time)) / 60::double precision)::numeric, 2) AS total_dwell_time,
count(cv.sensor_id) AS visitors,
cv.doorway_id,
cv.customer_id,
cv.centre_id,
cv.gender,
cv.postcode,
cv.age_group,
cv.no_children,
cv.no_cars,
cv.marital_status,
cv.employment_status,
cv.shopping_frequency,
cv.house_income
FROM rollups.some_rollup cv
WHERE cv.dwell_time > '00:00:30'::interval
GROUP BY cv.datestamp, cv.doorway_id, cv.customer_id, cv.centre_id, cv.gender, cv.postcode, cv.age_group, cv.no_children, cv.no_cars, cv.marital_status, cv.employment_status, cv.shopping_frequency, cv.house_income
) AS t1
ON d.datestamp::date = t1.datestamp::date
WHERE d.datestamp >= $1 AND d.datestamp < $2
ORDER BY d.datestamp
ON CONFLICT (datestamp, doorway_id, customer_id, centre_id, gender, postcode, age_group, no_children, no_cars, marital_status, employment_status, shopping_frequency, house_income)
DO UPDATE SET visitors=excluded.visitors, total_dwell_time = excluded.total_dwell_time;$$
USING start_time, end_time;
END;
$BODY$;

PostgreSQL 10 function creation not working

I have the following query, which I've modified for my database tables/columns, but it is not working upon execution:
CREATE OR REPLACE FUNCTION delete_data_antique(resourceid integer)
RETURNS TABLE(metrics_values_id int4) AS $$
BEGIN
RETURN QUERY
delete from metrics_values
where resource_id = $1
and time < (current_timestamp - interval '38 day')
and id not in
(select id
from (select distinct on (time_week)
id, time, date_trunc('week', time) time_week
from metrics_values
where resource_id = $1
and time < (current_timestamp - interval '38 day')
order by time_week, time desc)
as first_in_week_versions)
returning id;
END;
$$ LANGUAGE 'plpgsql';
Error message is as follows
I'm new to creating functions in SQL, and have been reading the docs, but not sure where/how it's not actually working.