PostgreSQL 10 function creation not working - postgresql

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.

Related

stored function in postgres causing the time out

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.

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$;

pass parameters in crosstab query postgres

How to pass a parameter in crosstab query in postgresql.
Please refer the below function in postgresql
create function sp_nextfollowup_count_byweek(_from timestamp without time zone,_to timestamp without time zone)
returns table(userid integer,username character varying,day1 bigint,day2 bigint,day3 bigint,day4 bigint,day5 bigint,day6 bigint,day7 bigint)as
$BODY$
BEGIN
return query
with cte as(
select * from crosstab($$
select follow_up_by,next_follow_up_date,count(*) from sales_enquiry_follow_up where next_follow_up_date between _from and _to
group by follow_up_by,next_follow_up_date
order by follow_up_by,next_follow_up_date$$,$$select date::timestamp without time zone
from generate_series(
_from,
_to,
'1 day'::interval
) date$$)
as(id integer, dd bigint,dd1 bigint,dd2 bigint,dd3 bigint,dd4 bigint,dd5 bigint,dd6 bigint)
)
select cte.id,u.username,cte.dd,cte.dd1,cte.dd2,cte.dd3,cte.dd4,cte.dd5,cte.dd6 from cte left join users u on cte.id=u.id;
END;
$BODY$
language plpgsql;
I am getting this error column "_from" does not exist. How to solve this issue?
Because argument of crosstab is just string you must include arguments directly:
$$
select follow_up_by,next_follow_up_date,count(*) from sales_enquiry_follow_up where next_follow_up_date between $$ || quote_literal(_from) || $$ and $$ || quote_literal(_to) || $$
group by follow_up_by,next_follow_up_date
order by follow_up_by,next_follow_up_date$$

CREATE TABLE if not exists based on a SELECT statement

I want to create a table if it does not exists based on a select statement in PostgreSQL 9.2. When I use the below query, i get an error as mentioned below.
Query:
CREATE TABLE IF NOT EXISTS ccdb_archival.bills
SELECT *, now() AS archival_date
FROM ccdb.bills
WHERE bill_date::date >= current_date - interval '3 years' AND bill_date::date < current_date - interval '8 years';
Error:
ERROR: syntax error at or near "SELECT"
LINE 2: SELECT *, now() AS archival_date
Can someone suggest how can I achieve this.
I did get an alternate for this. I used the below mentioned code.
CREATE OR REPLACE FUNCTION ccdb_archival.ccdb_archival()
RETURNS void AS
$BODY$
BEGIN
CREATE TABLE IF NOT EXISTS ccdb_archival.bills (LIKE ccdb.bills INCLUDING ALL);
BEGIN
ALTER TABLE ccdb_archival.bills ADD COLUMN archival_date timestamp;
EXCEPTION
WHEN duplicate_column THEN RAISE NOTICE 'column archival_date already exists in ccdb_archival.bills.';
END;
INSERT INTO ccdb_archival.bills
SELECT *, now() AS archival_date
FROM ccdb.bills
WHERE bill_date::date >= current_date - interval '3 years' AND bill_date::date < current_date - interval '8 years';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

Return table type from a function in PostgreSQL

I have a function with RETURNS TABLE, and I want to return certain columns from my source table. When I execute the function, it gives no error but also returns no rows although it should.
What's wrong with my function?
CREATE OR REPLACE FUNCTION ccdb.fn_email_details_auto()
RETURNS TABLE (code integer, area smallint, action smallint
, flag smallint, ucount integer, view_cnt integer) AS
$BODY$
DECLARE
sec_col refcursor;
cnt integer;
sec_code ccdb.update_qtable%ROWTYPE;
BEGIN
SELECT COUNT(DISTINCT section_code) INTO cnt
FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day';
OPEN sec_col FOR
SELECT * FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day';
FOR i IN 1..cnt
LOOP
FETCH sec_col INTO sec_code;
PERFORM section_code, ddu_area, ddu_action, status_flag
, ccdb_ucount, ccdb_view_cnt
FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day'
AND section_code = sec_code.section_code
ORDER BY ddu_area, ddu_action;
END LOOP;
CLOSE sec_col;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
Your function is doing a lot of empty work.
You could replace the tedious and expensive explicit cursor with a FOR loop using a cursor implicitly. But don't bother, and radically simplify with a single query instead. Optionally wrapped into an SQL function:
CREATE OR REPLACE FUNCTION ccdb.fn_email_details_auto()
RETURNS TABLE (code integer, area smallint, action smallint, flag smallint
, ucount integer, view_cnt integer)
LANGUAGE sql AS
$func$
SELECT u.section_code, u.ddu_area, u.ddu_action, u.status_flag
, u.ccdb_ucount, u.ccdb_view_cnt
FROM ccdb.update_qtable u
WHERE u.entry_time >= now()::date - 1
AND u.entry_time < now()::date -- sargable!
ORDER BY u.section_code, u.ddu_area, u.ddu_action;
$func$;
Should be much faster while returning the same.
Also, use this:
WHERE u.entry_time >= now()::date - 1
AND u.entry_time < now()::date
instead of:
WHERE entry_time::date = now()::date - interval '1 day'
The alternative is "sargable" and can use a plain index on (entry_time), which is crucial for performance.
I was able to solve this issue by using a RETURN QUERY for the SELECT statement where I was using PERFORM.
The below mentioned query helped me achieve my requirement.
CREATE OR REPLACE FUNCTION ccdb.fn_email_details_auto()
RETURNS TABLE (code integer, area smallint, action smallint, flag smallint, ucount integer, view_cnt integer) AS
$BODY$
DECLARE
sec_col refcursor;
cnt integer;
sec_code ccdb.update_qtable%ROWTYPE;
BEGIN
SELECT COUNT(DISTINCT section_code)
INTO cnt
FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day';
OPEN sec_col FOR
SELECT DISTINCT ON (section_code)* FROM ccdb.update_qtable WHERE entry_time::date = now()::date - interval '1 day';
FOR i IN 1..cnt
LOOP
FETCH sec_col INTO sec_code;
RETURN QUERY
SELECT section_code, ddu_area, ddu_action, status_flag, ccdb_ucount, ccdb_view_cnt
FROM ccdb.update_qtable
WHERE entry_time::date = now()::date - interval '1 day' AND section_code = sec_code.section_code
ORDER BY ddu_area, ddu_action;
END LOOP;
CLOSE sec_col;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;