ADD_MONTHS in PostgreSQL - postgresql

I have issue change ADD_MONTHS Oracle to PostgreSQL.
I have Oracle query like this :
ADD_MONTHS (to_date(to_char(start_billdate,'DD-MM-YYYY'),'DD-MM-YYYY'),
(processed_num*periodvalue)
)
So how to implement that query to PostgreSQL?

One option might be to multiply number of months (processed_num * periodvalue) with the interval of 1 month and add that to start_billdate:
start_billdate + (interval '1 month' * processed_num * periodvalue);

You can construct an interval with the contents of the column:
start_billdate + make_interval(months => processed_num*periodvalue)

Related

How can I select data between an hour range without using extract?

I'm trying to select data between an hour range, without using extract to be able to use the indexes in the table. I do the query like this:
SELECT *
FROM activities
WHERE "dateParam"::timestamp >= '21:00:00'::timestamp
AND "dateParam"::timestamp <= '23:00:00'::timestamp;
But I get an 22007 state and it doesnt work, how could I be able to do it?
Try:
SELECT *
FROM activities
WHERE "dateParam"::timestamp >= current_date + '21:00:00'::time
AND "dateParam"::timestamp <= current_date + '23:00:00'::time;
A timestamp needs a date portion so add it.

PostgreSQL query for elapsed interval

I am trying to query PostgreSQL database for rows where interval has elapsed from the last run. Main columns for this purpose are processed_at as timestamptz and frequency (in minutes) as integer.
I am failing with operators, since not many of them can operate together timestamp & integer.
Can someone please propose a query that would solve this? Thank you very much for help
From here Date/time operators:
timestamp + interval → timestamp
Add an interval to a timestamp
timestamp '2001-09-28 01:00' + interval '23 hours' → 2001-09-29 00:00:00
select now() + (10::varchar || ' min')::interval;
?column?
-------------------------------
2021-10-15 09:05:37.927163-07
--Or in your case. If I'm following you are adding the interval.
select processed_at + (frequency::varchar || ' min')::interval;
The query takes the integer value of minutes and converts it to an interval of minutes that can be added to the timestamp.
Further explanation, || is the Postgres concatenation operator and ::varchar, ::interval are casting shorthand.
UPDATE
I keep forgetting about the make_*() functions for date/time/interval
--A shorter version
select processed_at + make_interval(mins => frequency);
Saves all the casting.

Convert string to Date then minus some months

I'm using Postgres with Rails. I've two string fields which store date like this, field_1 = "2020.06.09", I 'm comparing them with TO_DATE function and they are working finr but now I want to compare them after adding 5 months in one field. What I've tried so far is:
select * from users as u where TO_DATE(u.field_1, 'YYYY MM DD') > DATEADD(MONTH, 5, TO_DATE(u.field_2, 'YYYY MM DD'))
can anyone help me on this?
Thanks in advance.
There is no dateadd() in Postgres (or standard SQL), you need to add an interval:
select *
from users u
where TO_DATE(u.field_1, 'YYYY MM DD') > to_date(u.field_2, 'yyyy mm dd') + interval '5 month';

Add_months function error based on postgres database

I tried to ruh this query in postgres :
Select to_char((select add_months (to_date ('10/10/2019', 'dd/mm/yyyy'), '11/11/2019') ) , 'dd/mm/yyyy') as temp_date
I got an error :
Function add_months (date, unknown) does not exist
Hint: no function matches the given name and argument types. You might need to add explicit type casts.
Please help
As documented in the manual there is no add_months function in Postgres
But you can simply add an interval:
select to_date('10/10/2019', 'dd/mm/yyyy') + interval '10 months'
If you need to format that date value to something:
select to_char(to_date('10/10/2019', 'dd/mm/yyyy') + interval '10 months', 'yyyy-mm-dd')
No one, even running on Oracle, has run the original query- at least not successfully. It appears that query is expecting to add two months together (in this case Oct and Nov). That is not what the function does. It adds an integer number of months to the specified date and returns the resulting date. As indicated in Postgres just adding the desired interval. However, if you have many occurrences ( like converting) of this the following implements a Postgres version.
create or replace function add_months(
date_in date
, n_months_in integer)
returns date
language sql immutable strict
as
$$
-- given a date and an integer for number of months return the calendar date for the specified number of months away.
select (date_in + n_months_in * interval '1 month')::date
$$ ;
-- test
-- +/- 6 months from today.
select current_date "today"
, add_months(current_date,6) "6 months from now"
, add_months(current_date,-6) "6 months ago"
;

Using a variable period in an interval in Postgres

I have a relation that maintains monthly historical data. This data is added to the table on the last day of each month. A service I am writing can then be called specifying a month and a number of months prior for which to retrieve the historical data. I am doing this by creating startDate and endDate variables, and then returning data between the two. The problem I am having is that startDate is a variable number of months before endDate, and I cannot figure out how to use a variable period in an interval.
Here is what I have:
DECLARE
endDate TIMESTAMP := (DATE_TRUNC('MONTH',$2) + INTERVAL '1 MONTH') - INTERVAL '1 DAY';
startDate TIMESTAMP := endDate - INTERVAL $3 'MONTH';
I know that the line for startDate is not correct. How is this properly done?
Use this line:
startDate TIMESTAMP := endDate - ($3 || ' MONTH')::INTERVAL;
and note the space before MONTH.
Basically: You construct a string with like 4 MONTH and cast it with ::type into a proper interval.
Edit: I' have found another solution: You can calculate with interval like this:
startDate TIMESTAMP := endDate - $3 * INTERVAL '1 MONTH';
This looks a little bit nicer to me.
This code has nothing directly to do with your situation, but it does illustrate how to use variables in INTERVAL arithmetic. My table's name is "calendar".
CREATE OR REPLACE FUNCTION test_param(num_months integer)
RETURNS SETOF calendar AS
$BODY$
select * from calendar
where cal_date <= '2008-12-31 00:00:00'
and cal_date > date '2008-12-31' - ($1 || ' month')::interval;
$BODY$
LANGUAGE sql VOLATILE
COST 100
ROWS 1000;
The most readable way I have found to pass a variable time period to Postgres is similar to A.H.'s answer: by multiplying by an integer. But this can be done without a cast.
Python example (with sqlalchemy and pandas):
import pandas as pd
import sqlalchemy as sa
connection = sa.create_engine(connection_string)
df = pd.read_sql(
sa.text('''
select * from events
where
event_date between now() - (interval '1 day' * :ndays) and now()
limit 100;
'''),
connection,
params={'ndays': 100}
)
The number of days (ndays) is passed as an integer from within Python - so unintended consequences are less likely.
My approach is like this.. It gives me option to set specific date or a relative range.
create or replace function search_data(_time_from timestamptz default null, _last_interval text default null)
returns setof journal
language plpgsql as
$$
begin
return query
select *
from journal
where created >= case
when _time_from is not null
then _time_from
else now() - _last_interval::interval end;
end;
$$;
While the above accepted answer is fine, it's a little bit antiquated - requiring a bit more mental energy to read than needed if you're running on Postgres 9.4+.
Old Way (Postgres Versions < 9.4)
startDate TIMESTAMP := endDate - $3 * INTERVAL '1 MONTH';
New Way (Postgres 9.4+)
startDate TIMESTAMP := endDate - MAKE_INTERVAL(MONTHS => $3);
If you are on Postgres 9.4+, the new MAKE_INTERVAL() function seems much more readable - probably why they created it.
If you want something you can run in your editor, here are a couple of
examples (I substituted the original variable binding $3 with the number 2 for an example of 2-months prior to the current date).
SELECT CURRENT_DATE - 2 * INTERVAL '1 MONTH';
SELECT CURRENT_DATE - MAKE_INTERVAL(MONTHS => 2);