Issue with storing date value into a variable in postgresql script - postgresql

I'm trying to fetch first date and last date of month and trying to store the values into the start_date and end_date variables. But, it is not happening?! The output just says DO.
do $$
declare
start_date DATE;
end_date DATE;
begin
-- select the first date of the month
select cast(date_trunc('month', current_date) as date)
into start_date
raise notice 'The first date of the month is: %', start_date;
-- select the last date of the month
select (date_trunc('month', now()) + interval '1 month - 1 day')::date as end_of_month
into end_date
raise notice 'The last date of the month is: %', end_date;
-- show the first and last date of the month
end $$;

Related

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

I can't subtract days from current_date parametrically in PostgreSQL

I want to subtract some days from the current date and insert it into a table. If I write the number of days directly into the code it works. So this works.
do $$
DECLARE
myDate Date;
BEGIN
myDate = current_date - interval '10' day;
insert into myTable (myDate) values (myDate);
end $$;
The problem is that I want to put the number of days in a variable to make it parametric. But I can't. In fact the following doesn't work:
do $$
DECLARE
myDate Date;
daysAgo character varying := '10';
BEGIN
myDate = current_date - interval daysAgo day;
insert into myTable (myDate) values (myDate);
end $$;
You can subtract a number of days (integer) from a date:
do $$
DECLARE
myDate Date;
daysAgo int := 10;
BEGIN
myDate = current_date - daysAgo;
insert into myTable (myDate) values (myDate);
end $$;
Date/Time Functions and Operators.
Alternatively if you need to do this not just for days, you can do certain math operations on intervals:
do $$
declare
minutes int4 := 34;
begin
raise notice '%', interval '1 minute' * minutes;
raise notice '%', interval '15 minute' / minutes;
end;
$$;
-- outpus:
-- 00:34:00
-- 00:00:26.470588
This also works (with the make_interval function) but is more verbouse than Klin's solution.
do $$
DECLARE
myDate Date;
daysAgo int := 10;
BEGIN
myDate = current_date - make_interval(days=>daysAgo);
insert into myTable (myDate) values (myDate);
end $$;

how to get the next date for a day of month

thanks for reading, this is the situation
I have a current_date and a day of month, so i need to know what will be the next date for this day of month, having in mind that some month don't have 30 and 31.
Example:
current_date = '2018-09-24'
day_of_week = 31
Expected result: '2018-12-31'
Currently i have this:
create or replace function next_diff(vals int[], current_val int) returns int as
$$
declare v int;
declare o int := vals[1];
begin
foreach v in array vals loop
if current_val >= o and current_val < v then
return v - current_val;
end if;
o := v;
end loop;
return vals[1] - current_val;
end;
$$ language plpgsql;
and this:
create or replace function next_day_of_month(days_of_month int[], curr_date date) returns date as
$$
declare cur_dom int := extract(day from curr_date);
declare next_diff int := next_diff(days_of_month, cur_dom);
begin
if next_diff < 0 then
curr_date := curr_date + '1 months'::interval;
end if;
curr_date := curr_date + (next_diff || 'days')::interval;
return curr_date;
end;
$$ language plpgsql;
but for this calling:
select next_day_of_month(array[31], '2018-09-24');
i am getting:
"2018-10-01"
Extra example
If i have this value
current_date = '2018-02-01'
day_of_week = 31
i will need the next month with 31th but i can't get '2018-02-31' because February don't have 31th then i should get '2018-02-31' because March have 31th.
Conclusion
if the month don't have the specified day must ignore the month and jump to the next.
thanks for all
Final method
Using Carlos Gomez answer, i create this PostgreSQL function and work perfectly:
create or replace function next_day_date(curr_date date, day_of_month int) returns date as
$$
declare next_day date;
begin
SELECT next_day_date into next_day FROM (
SELECT make_date_nullable(EXTRACT(year from n.month)::int, EXTRACT(month from n.month)::int, day_of_month) AS next_day_date
FROM (
SELECT generate_series(curr_date, curr_date + '3 months'::interval, '1 month'::interval) as month
) n
) results
WHERE results.next_day_date IS NOT NULL and results.next_day_date > curr_date LIMIT 1;
return next_day;
end;
$$ language plpgsql;
just add other filter in where clause and results.next_day_date > curr_date to prevent get the same or previous values for specified date
Thanks everyone for helping
Thenks Carlos you are the best
Gracias carlos eres el mejor :)
Your examples don't really match up but I think I know what you are trying to solve for (your first example result should be '2018-10-31' since October has 31 days and your second example result should be '2018-03-31'). It seems that given a date and a day of month you want to find the next month that has that day of month. To do this, I would do the following:
This function just wraps make_date to let it return null since it throws an exception if a date given to it is out of bounds (like February 30).
CREATE OR REPLACE FUNCTION make_date_nullable(year int, month int, day int)
RETURNS date as $$
BEGIN
RETURN make_date(year, month, day);
EXCEPTION WHEN others THEN RETURN null;
END;
$$ language plpgsql;
This SELECT first generates the next three months starting with the current one, then makes date out of them with your provided day_of_month and finally gets the first one that isn't null (exists according to postgresql.
SELECT next_day_date FROM (
SELECT make_date_nullable(EXTRACT(year from n.month)::int, EXTRACT(month from n.month)::int, day_of_month) AS next_day_date
FROM (
SELECT generate_series(current_date, current_date + '3 months'::interval, '1 month'::interval) as month
) n
) results
WHERE results.next_day_date IS NOT NULL LIMIT 1;
Hope this helps!

Get data for every second in interval

I want to write a query which will results value between timestamp at every second and if value for particular time stamp is not there then it should result zero.for example
Start date time - 27/7/2015 10:00:00
End date time - 27/7/2015 10:05:00
Then result should be
27/7/2015 10:00:00 10 [start date time]
27/7/2015 10:00:01 19
27/7/2015 10:00:02 23
27/7/2015 10:00:03 0 [Value not present in table for this timestamp]
27/7/2015 10:00:04 45
27/7/2015 10:00:05 0 [Value not present in table for this timestamp]
...
27/7/2015 10:05:00 42 [end date time ]
I am trying this query but not getting desired result
SELECT CAST(date_trunc('second', CAST(to_timestamp(t1.timestamp_col,'DD-MM-YYYY HH24:MI:SS')as timestamp without time zone) + interval '1 second') as text)
, NULLIF(t1.y_temperature_col,'00')
FROM historical_trend_data t1
WHERE CAST(to_timestamp(t1.timestamp_col,'DD-MM-YYYY HH24:MI:SS') as timestamp without time zone) BETWEEN CAST(to_timestamp('28/7/2015 10:00:00','DD-MM-YYYY HH24:MI:SS')as timestamp without time zone) AND CAST(to_timestamp('28/7/2015 18:00:00','DD-MM-YYYY HH24:MI:SS') as timestamp without time zone);
This is the Function
CREATE OR REPLACE FUNCTION timestampwise_sp2(IN startdatetime text, IN enddatetime text,
OUT x_time_col text, OUT temperature text)
RETURNS SETOF record AS $BODY$
BEGIN
return query
with simul_data as(
SELECT generate_series(startdatetime::timestamp,
enddatetime::timestamp, '1 Seconds') As x_time_col
)
Select simul_data.x_time_col::text, coalesce(t1.y_temperature_col, '0') AS temperature
from historical_trend_data t1
LEFT JOIN simul_data ON CAST(to_timestamp(t1.timestamp_col,'DD-MM-YYYY HH24:MI:SS') as timestamp without time zone) = simul_data.x_time_col;
END; $BODY$ LANGUAGE plpgsql VOLATILE;
but it is not resulting the desired result
SELECT s.sec AS obs_time, coalesce(t1.y_temperature_col, 0) AS temperature
FROM generate_series('28-07-2015 10:00:00'::timestamp,
'28-07-2015 18:00:00'::timestamp, '1 minute') AS s(sec)
LEFT JOIN t1 ON timestamp_col = s.sec;
The generate_series() function returns a set of records in a sequence, in this case second intervals from a starting point to an ending point. That is left-joined to your actual data and any NULL values for the temperature are converted to 0 with the coalesce() function.
In function form it would be like this:
CREATE FUNCTION timestampwise_sp2(startdatetime timestamp, enddatetime timestamp) AS $$
SELECT to_char(simul_data.x_time_col, 'DD/MM/YYYY HH24:MI:SS') AS x_time_col,
coalesce(t1.y_temperature_col, '0') AS temperature
FROM generate_series($1, $2, '1 second') AS simul_data(x_time_col)
LEFT JOIN historical_trend_data t1
ON to_timestamp(t1.timestamp_col,'DD/MM/YYYY HH24:MI:SS') = simul_data.x_time_col;
$$ LANGUAGE sql STRICT;
Note that this is a sql language function, which is faster than plpgsql.

PostgreSQL: month := interval '30 days';

Trying to delete records older than 1 month from 2 tables, where 1 references the "id" column in another:
create or replace function quincytrack_clean()
returns void as $BODY$
begin
month := interval '30 days';
delete from hide_id
where id in
(select id from quincytrack
where age(QDATETIME) > month);
delete from quincytrack
where age(QDATETIME) > month;
end;
$BODY$ language plpgsql;
but this fails with:
ERROR: syntax error at or near "month"
LINE 1: month := interval '30 days'
^
QUERY: month := interval '30 days'
CONTEXT: SQL statement in PL/PgSQL function "quincytrack_clean" near line 2
I'm reading the doc, but don't understand what's wrong with my declaration...
You need to declare the variable 'month', viz.:
declare
month interval;
begin
month := interval '30 days';
end;
Also, you might want to re-examine your "where" criteria. If QDATETIME is an indexed column, I don't think it will use the index, whereas QDATETIME < (now() - month) would.
You need to declare the variable before you can use it.
...
DECLARE
month INTERVAL;
BEGIN
month := interval '30 days';
...
But I would avoid using variable names that are reserved words or internal function names.