Recurring future date at every 3 month after create date in PostgreSQL - postgresql

I am looking for a function in PostgreSQL which help me to generate recurring date after every 90 days from created date
for example: here is a demo table of mine.
id date name
1 "2020-09-08" "abc"
2 "2020-09-08" "xyz"
3 "2020-09-08" "def"
I need furure date like 2020-12-08, 2021-03-08, 2021-06-08, and so on

First it's important to note that, if you happen to have a date represented as text, then you can convert it to a date via:
SELECT TO_DATE('2017-01-03','YYYY-MM-DD');
So, if you happen to have a text as an input, then you will need to convert it to date. Next, you need to know that if you have a date, you can add days to it, like
SELECT CURRENT_DATE + INTERVAL '90 day';
Now, you need to understand that you can use dynamic variables, like:
select now() + interval '1 day' * 180;
Finally, you will need a temporary table to generate several values described as above. Read more here: How to return temp table result in postgresql function
Summary:
create a function
that generates a temporary table
where you insert as many records as you like
having the date shifted
and converting text to date if needed

You can create a function that returns a SETOF dates/timestamps. The below function takes 3 parameters: a timestamp, an interval, the num_of_periods desired. It returns num_of_periods + 1 timestamps, as it returns the original timestamp and the num_of_periods each the specified interval apart.
create or replace
function generate_periodic_time_intervals
( start_date timestamp
, period_length interval
, num_of_periods integer
, out gen_timestamp timestamp
)
returns setof timestamp
language sql
immutable strict
as $$
select (start_date + n * period_length)::timestamp
from generate_series(0,num_of_periods) gs(n)
$$;
For your particular case to timestamp/date as necessary. The same function would work for your case with the interval specified as '3 months' or of '90 days'. Just a note the interval specified can be any valid INTERVAL data type. See here. It also demonstrates the difference between 3 months and 90 days.

Related

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

need tsql code for time intervel and the values should go to that particular interval the original time is in epoch format

I have to add column to table.
The column value is calculated based on the column already present in the table.
I have to get time-stamp (column already present) and then group them into 5 min time-slots.
E.g: if the time is:
13:03/13:02 then it should go as 13:00;
13:53/13:52 then it should go as 13:50;
13:21 then should go as 13:20 and so on
PS: basically I have to get time stamp in epoch (UNIX time stamp)format [the table has values in epoch as well as in regular time stamp]
So what I'm seeing is that you have times and you need to round them down to the nearest 5 minute increment. Try this:
DECLARE #table TABLE (times TIME)
INSERT INTO #table
VALUES ('13:03'),
('13:02'),
('13:53'),
('13:52'),
('13:21');
SELECT times,
DATEADD(MINUTE,-DATEDIFF(MINUTE,0,times) % 5,times) five_minute_increments
FROM #table
Results:
times five_minute_increments
---------------- ----------------------
13:03:00.0000000 13:00:00.0000000
13:02:00.0000000 13:00:00.0000000
13:53:00.0000000 13:50:00.0000000
13:52:00.0000000 13:50:00.0000000
13:21:00.0000000 13:20:00.0000000
Epoch Version
DECLARE #epoch BIGINT;
--Epoch is the seconds since Jan 1,1970
SET #epoch = DATEDIFF(SECOND,'1970-01-01','2015-04-01 12:06:00.000');
SELECT CAST(DATEADD(SECOND,#epoch - (#epoch %300),'1970-01-01 00:00:00.000') AS TIME) AS epochTimes
Results:
12:05:00.0000000
create table epoch1(epoch int not null,
epoch_date as dateadd(s,epoch,'19700101'))
insert epoch1(epoch) values(1331070999)
insert epoch1(epoch) values(1331070956)
insert epoch1(epoch) values(1331071998)
insert epoch1(epoch) values(1331071999)
select DATEADD(MINUTE,-DATEDIFF(MINUTE,0,dateadd(MINUTE,epoch,'19700101')) % 5,dateadd(MINUTE,epoch,'19700101')) as human ,DATEDIFF(MINUTE, '1970-01-01 00:00:00', DATEADD(MINUTE,-DATEDIFF(MINUTE,0,dateadd(MINUTE,epoch,'19700101')) % 5,dateadd(MINUTE,epoch,'19700101'))) as timeinterval
from epoch1

TSQL update Datetime with Random Value between 2 Dates

What's the easiest way to update a table that contains a DATETIME column on TSQL with RANDOM value between 2 dates?
I see various post related to that but their Random values are really sequential when you ORDER BY DATE after the update.
Assumptions
First assume that you have a database containing a table with a start datetime column and a end datetime column, which together define a datetime range:
CREATE DATABASE StackOverflow11387226;
GO
USE StackOverflow11387226;
GO
CREATE TABLE DateTimeRanges (
StartDateTime DATETIME NOT NULL,
EndDateTime DATETIME NOT NULL
);
GO
ALTER TABLE DateTimeRanges
ADD CONSTRAINT CK_PositiveRange CHECK (EndDateTime > StartDateTime);
And assume that the table contains some data:
INSERT INTO DateTimeRanges (
StartDateTime,
EndDateTime
)
VALUES
('2012-07-09 00:30', '2012-07-09 01:30'),
('2012-01-01 00:00', '2013-01-01 00:00'),
('1988-07-25 22:30', '2012-07-09 00:30');
GO
Method
The following SELECT statement returns the start datetime, the end datetime, and a pseudorandom datetime with minute precision greater than or equal to the start datetime and less than the second datetime:
SELECT
StartDateTime,
EndDateTime,
DATEADD(
MINUTE,
ABS(CHECKSUM(NEWID())) % DATEDIFF(MINUTE, StartDateTime, EndDateTime) + DATEDIFF(MINUTE, 0, StartDateTime),
0
) AS RandomDateTime
FROM DateTimeRanges;
Result
Because the NEWID() function is nondeterministic, this will return a different result set for every execution. Here is the result set I generated just now:
StartDateTime EndDateTime RandomDateTime
----------------------- ----------------------- -----------------------
2012-07-09 00:30:00.000 2012-07-09 01:30:00.000 2012-07-09 00:44:00.000
2012-01-01 00:00:00.000 2013-01-01 00:00:00.000 2012-09-08 20:41:00.000
1988-07-25 22:30:00.000 2012-07-09 00:30:00.000 1996-01-05 23:48:00.000
All the values in the column RandomDateTime lie between the values in columns StartDateTime and EndDateTime.
Explanation
This technique for generating random values is due to Jeff Moden. He wrote a great article on SQL Server Central about data generation. Read it for a more thorough explanation. Registration is required, but it's well worth it.
The idea is to generate a random offset from the start datetime, and add the offset to the start datetime to get a new datetime in between the start datetime and the end datetime.
The expression DATEDIFF(MINUTE, StartDateTime, EndDateTime) represents the total number of minutes between the start datetime and the end datetime. The offset must be less than or equal to this value.
The expression ABS(CHECKSUM(NEWID())) generates an independent random positive integer for every row. The expression can have any value from 0 to 2,147,483,647. This expression mod the first expression gives a valid offset in minutes.
The epxression DATEDIFF(MINUTE, 0, StartDateTime) represents the total number of minutes between the start datetime and a reference datetime of 0, which is shorthand for '1900-01-01 00:00:00.000'. The value of the reference datetime does not matter, but it matters that the same reference date is used in the whole expression. Add this to the offset to get the total number of minutes between the reference datetime.
The ecapsulating DATEADD function converts this to a datetime value by adding the number of minutes produced by the previous expression to the reference datetime.
You can use RAND for this:
select cast(cast(RAND()*100000 as int) as datetime)
from here
Sql-Fiddle looks quite good: http://sqlfiddle.com/#!3/b9e44/2/0

Postgresql function for checking date ranges

I'm not sure how to check for date ranges using a postgres function. What I want to do is check if a date falls within a certain range (with leeway of a week before the starting date)
So basically, I want to check if a date is between 7 days before to current date, and if so I'll return the id of that row.
create or replace function eight(_day date) returns text as $$
declare
r record;
check alias for $1;
startDate date;
begin
for r in
select * from terms
order by starting;
loop
startDate := r.starting;
if check between (..need help to create 7 days before startDate) and startDate return r.id;
end;
$$ language plpgsql;
I also have to check if the previous record's ending date collides with the startDate - 7days. How would I check the previous record?
Sounds like you want to use an interval:
startDate - interval '...'
I won't say any more than this since you're doing homework.
Dates work with integer math.
startdate - 8 is equivalent to (startdate::timestamp - '8 days'::interval)::date

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