PostgreSQL: month := interval '30 days'; - postgresql

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.

Related

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

make_date function does not exist in plpgsql

I've got a plpgsql function. I need to take the date 5 days from today, and then divide month into "fives" to takte the start of "last five". The problem is thay make_date does not exist in the posgres version that is used on the server....
create or replace function getFirstDayOfFive()
returns timestamp with time zone as $$
declare
firstDay timestamp;
startOp timestamp;
begin
startOp = now() - interval '5 day';
SELECT
make_date(
date_part('year', startOp)::int,
date_part('month', startOp)::int,
greatest(
floor(date_part('day', startOp) / 5) * 5,
1
)::int
)
INTO firstDay;
RETURN firstDay;
end;
$$
language plpgsql;
It worked fine last week, but now I got an error when I call it
ERROR: BŁĄD: function make_date(integer, integer, integer) does not exist
LINE 2: make_date(
^
HINT: There is no function matching provided name and arguments. Maybe you should cast data.
QUERY: SELECT
make_date(
date_part('year', startOp)::int,
date_part('month', startOp)::int,
greatest(
floor(date_part('day', startOp) / 5) * 5,
1
)::int
)
CONTEXT: PL/pgSQL function "getfirstdayoffive" line 7 at wyrażenie SQL
SQL state: 42883
What happened that earlier it worked and now it gives error?
[Edit]
I found out that make_date is available from postgresQL 9.4, but on the server there is posthresQL 9.1 is there any way to do the same in this old version od DB? I'm trying to replace the make_date with something like
create or replace function getFirstDayOfFive()
returns timestamp with time zone as $$
declare
firstDay timestamp;
startOp timestamp;
begin
startOp = now() - interval '5 day';
SELECT
date to_char(startOp, 'YYYY-MM-')||to_char(greatest(
floor(date_part('day', startOp) / 5) * 5,
1
)::int)
INTO firstDay;
RETURN firstDay;
end;
$$
language plpgsql;
I think you can simplify this by simply adding the desired number of days to the start of the month. Apparently you only want a date so I would also recommend to change the return type to date
create or replace function getfirstdayoffive()
returns date
as
$$
select date_trunc('month', current_date - 5)::date
+ (greatest(floor(extract(day from current_date - 5) / 5) * 5, 1))::int - 1;
$$
language sql
stable;

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!

What is the difference between INTERVAL '0 days' and '0 days'::INTERVAL in postgres

I am trying to create a basic table with a list of dates in psql using a loop. The code is shown below
DO $$
DECLARE counter INTEGER :=0;
date_interval VARCHAR(250) :='0 DAYS';
BEGIN
DROP TABLE IF EXISTS temp_dates;
CREATE TABLE temp_dates (
date DATE
);
WHILE counter < 12 LOOP
date_interval := counter || ' DAYS';
INSERT INTO temp_dates
--SELECT DATE_TRUNC('DAYS', CURRENT_DATE) - date_interval :: INTERVAL;
SELECT (DATE_TRUNC('DAYS', CURRENT_DATE)) - (INTERVAL date_interval);
counter := counter + 1;
END LOOP;
END $$;
In the above query
SELECT DATE_TRUNC('DAYS', CURRENT_DATE) - date_interval :: INTERVAL;
works. But the following does not work
SELECT (DATE_TRUNC('DAYS', CURRENT_DATE)) - (INTERVAL date_interval);
Basically the only difference date_interval :: INTERVAL vs INTERVAL date_interval
But it works when I directly do something like INTERVAL '2 days' but it fails when I use a variable.
So my question is two fold
What is the difference between INTERVAL '2 days' and '2 days'::INTERVAL. From what I understand both are typecasting.
Why does INTERVAL '2 days' work but it fails when using a variable
I am sure I am missing something fairly obvious when it comes to escaping quotation marks but I am not sure. Any help would be greatly appreciated.
As the documentation states clearly, the syntax
INTERVAL '0 days'
is for constants, that is, the string has to be a literal.
It is not a syntax for type casts like '0 days'::interval, which allow you to change the type of an arbitrary expression.

LAST_DAY function in postgres

Is there any function(s) in postgres equivalent to Oracle function LAST_DAY().
I need to get last day in postgres (including month and year)
Well, In postgres, it seems there's no such function equivalent to LAST_DAY() available in oracle.
If you need to, you can have your own in the following ways as a
Select Query
SELECT (date_trunc('MONTH', now()) + INTERVAL '1 MONTH - 1 day')::date;
plsql Function
CREATE OR REPLACE FUNCTION last_day(date)
RETURNS date AS
$$
SELECT (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day')::date;
$$ LANGUAGE 'sql'
IMMUTABLE STRICT;
Hope this helps.
create or replace funCtion last_day(fromdt anyelement)
returns date as
$BODY$
SELECT (date_trunc('MONTH', cast(fromdt as date)) + INTERVAL '1 MONTH - 1 day')::date;
$BODY$
LANGUAGE sql VOLATILE
COST 100;
ALTER FUNCTION last_day(anyelement)
OWNER TO postgres;