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