postgresql: use timestamp variable within pl pgsql - postgresql

Im new using pl pgsql. I want to concatenate two variables but im gotting always same error: time_ variable is not known
Let's say that date_ is of type date and time_ is of type time. The error came from this row:
sum(extract(epoch from (least(s.end, gs.date_+time_) - greatest(s.beg, gs.date_))) / 60) as Timing
My code is below
delcare
time_ time;
Begin
execute $$SELECT CURRENT_TIMESTAMP::time FROM $$||result_table INTO time_;
execute $$SELECT MAX(date_) FROM $$||result_table INTO max_date;
IF max_date is not NULL THEN
execute $$DELETE FROM $$||result_table||$$ WHERE date_ >= $$||quote_literal(max_date);
ELSE
max_date := 'XXXXXXX';
end if;
execute $$
INSERT INTO $$result_table$$
(Id, gs.date_, TIME, timing)
SELECT * from (
select
Id, gs.date_,
(case
When TRIM(set) ~ '^OPT[0-9]{3}/MINUTE/$'
Then 'minute'
When TRIM(set) ~ '^OPT[0-9]{3}/SECOND/$'
Then 'second' as TIME,
sum(extract(epoch from (least(s.end, gs.date_+time_) -
greatest(s.beg, gs.date_)
)
) / 60) as Timing
from source s cross join lateral
generate_series(date_trunc('day', s.beg), date_trunc('day',
least(s.end,
CASE WHEN $$||quote_literal(max_date)||$$ = 'XXXXXXX'
THEN (current_date)
ELSE $$||quote_literal(max_date)||$$
END)
), interval '1 day') gs(date_)
where ( (beg, end) overlaps ($$||quote_literal(max_date)||$$'00:00:00', $$||quote_literal(max_date)||$$'23:59:59'))
group by id, gs.date_, TIME
) as X
where ($$||quote_literal(max_date)||$$ = X.date_ and $$||quote_literal(max_date)||$$ != 'XXXXXXX')
OR ($$||quote_literal(max_date)||$$ ='XXXXXXX')

Dynamic SQL should be generated through format() and parameters should not be passed as string literals, but through placeholders and using.
Your code is really hard to read, incomplete and there are some substantial syntax errors which stem from e.g. a missing END for the CASE and parentheses not properly paired. So the following code might still contain some errors as I apparently have no way of testing it.
But as your main SELECT does not seem to use dynamic SQL at all, all the quote_literal() and string concatenation is unnecessary, just use the variables directly.
As max_date is supposed to be a date value you can assign the string 'XXXXX' to it, but if you use the max_date directly, you can get rid of that check as far as I can tell.
declare
time_ time;
max_date date;
result_table text := 'contract_frequency';
table_schema text := 'public';
Begin
time_ := localtime;
execute format('SELECT MAX(date_) FROM %I.%I', table_schema, result_table) into INTO max_date;
IF max_date is not NULL THEN
execute format('DELETE FROM %I.%I WHERE date_ >= $1', table_schema, result_table) using max_date;
ELSE
-- you replace XXXX with current_date in the CASE expression
-- later on, so using current_date here seems the right thing to do
max_date := current_date;
end if;
SELECT *
from (
select
Id, gs.date_,
case
When TRIM(set) ~ '^OPT[0-9]{3}/MINUTE/$' Then 'minute'
When TRIM(set) ~ '^OPT[0-9]{3}/SECOND/$' Then 'second' as TIME,
end
sum(extract(epoch from (least(s.end, gs.date_+time_) - greatest(s.beg, gs.date_) ) ) / 60) as Timing
from source s
cross join lateral
generate_series(date_trunc('day', s.beg), date_trunc('day', least(s.end, max_date)), interval '1 day') gs(date_)
where (beg, end) overlaps (max_date::timestamp, max_date + time '23:59:59')
group by id, gs.date_, TIME
) as X
where (max_date = X.date_ and max_date <> current_date)
OR (max_date = current_date)
end;

Related

Calling a Function from SQL

This is my Function :
CREATE FUNCTION "UpdatePMPM"(nbr_mem_months integer, effectivedate date) RETURNS void
LANGUAGE plpgsql
AS
$$
DECLARE
ym varchar := to_char(effectivedate,'YYYYMM');
BEGIN
FOR r IN 1..nbr_mem_months LOOP
UPDATE elan.pmpm set mbrmonths = mbrmonths+1 where yyyyymm = ym;
effectivedate = effectivedate + interval '1 month';
ym=to_char(effectivedate,'YYYYMM');
END LOOP;
RETURN;
END
$$;
and when I call it manually from pgAdmin client it works perfectly.
Select public."UpdatePMPM"(5, '2016-04-01')
However, I am getting an error when calling it from within a SQL query:
select cast((extract(year from age(case when terminationdate is null then
CURRENT_DATE else terminationdate END ,effectivedate ))) *12 +
(extract(month from age(case when terminationdate is null then
CURRENT_DATE else terminationdate END ,effectivedate )) +1) as integer)
as "mbrmonths" ,effectivedate ,public."UpdatePMPM"(mbrmonths, effectivedate)
from elan.elig
order by 1
ERROR: column "mbrmonths" does not exist
LINE 5: ...s "mbrmonths" ,effectivedate ,public."UpdatePMPM"(mbrmonths,...
Any help would be appreciated.
You cannot use a column alias in the SELECT list to reference another column of the same list.
Either repeat the expression or use a subquery:
SELECT mbrmonths,
effectivedate,
public."UpdatePMPM"(mbrmonths, effectivedate)
FROM (select cast(
(extract(year from age(case when terminationdate is null
then CURRENT_DATE
else terminationdate
END,
effectivedate)
)) *12 +
(extract(month from age(case when terminationdate is null
then CURRENT_DATE
else terminationdate
END,
effectivedate
)) +1
as integer) as "mbrmonths",
effectivedate,
from elan.elig) AS subq
order by 1;
The type of second argument is text, not date.
The Correct call is
Select public."UpdatePMPM"(5, '2016-04-01'::date)

What is the format for an `INTERVAL` parameter in a `format()` call

I'm updating an existing stored function, handling two additional parameters and inserting them, where required. Within the function, I INSERT a row into a table using a call to EXECUTE format(), something like this...
CREATE OR REPLACE PROCEDURE function_p (
p_name TEXT DEFAULT '',
p_step INT DEFAULT NULL,
p_project_duration INTERVAL DEFAULT '2W',
)
LANGUAGE plpgsql AS $$
BEGIN
EXECUTE format('
INSERT INTO table_2 (column_1, column_2, column_3, name, step)
SELECT one_column, two_column, three_column, %L, %s
FROM generate_series(now()::DATE, now()::DATE + %L, INTERVAL ''1 day'') d
CROSS JOIN table_1',
p_name, p_step, p_project_duration);
END;
$$
I know this is a pretty rubbish example, but it's pseudo-code, ok?! ;)
In the generate_series() call...
If I use format %L I get error "operator is not unique: date + unknown"
If I use format %I I get error "column "P14d" does not exist"
If I use format %s I get error "column "P14d" does not exist"
Don't pass strings as parameters, use placeholders and pass the correct data types. Only use the format() placeholders for the identifiers, pass the actual parameters with the using clause of the execute command:
CREATE OR REPLACE PROCEDURE function_p (
p_name TEXT DEFAULT '',
p_step INT DEFAULT NULL,
p_project_duration INTERVAL DEFAULT '2W',
)
LANGUAGE plpgsql AS $$
BEGIN
EXECUTE format('
INSERT INTO table_2 (column_1, column_2, column_3, name, step)
SELECT one_column, two_column, three_column, %L, %s
FROM generate_series($1, $2, $3) d
CROSS JOIN table_1', p_name, p_step)
using current_date, current_date + p_project_duration, interval '1 day';
END;
$$
It seems I've found a hack-fix/cludge/bodge, yay!
It works if I use the '%s' format and then cast it to INTERVAL.
FROM generate_series(now()::DATE, now()::DATE + ''%s''::INTERVAL, INTERVAL ''1 day'') d

how can we declare a variable in cursor in postgresql

I want to store the result of fetch into a variable how we can do that in postgresql?
I also tried by creating a function it isn't working
code :
begin work;
DECLARE
cur_films CURSOR
FOR select CURRENT_DATE + i date_
FROM generate_series(date '2019-11-11'- CURRENT_DATE, date '2019-11-15' - CURRENT_DATE ) i;
fetch forward 2 from cur_films;
close cur_films;
commit work;
If you want to pass all values generated by your query to a function or procedure, you can aggregate everything into an array, then pass that array to the function:
DECLARE
l_dates date[];
begin
select array_agg(g.dt::date)
into l_dates
from generate_series(date '2019-11-11', date '2019-11-15', interval '1 day') as g(dt);
perform your_function(l_dates);
end;
But you wouldn't need PL/pgSQL for that. This can also be done in plain SQL:
with input as (
select array_agg(g.dt::date) as dates
from generate_series(date '2019-11-11', date '2019-11-15', interval '1 day') as g(dt)
)
select *
from your_function((select dates from input));

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.