Generate series of quarters in Postgresql - postgresql

I need to generate series of quarters, given start date and end date. I know of generate_series(), but it just does not work with quarter:
SELECT * FROM generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 quarter');
What makes quarter so special? Otherwise generate_series() works with pretty much everything, from microseconds to millenium:
select * from generate_series('2008-01-01 00:00'::timestamp,'2008-01-01 00:00:00.001', '1 microsecond');
select * from generate_series('2008-01-01 00:00'::timestamp,'2008-01-01 00:01', '1 second');
select * from generate_series('2008-01-01 00:00'::timestamp,'2008-01-01 01:00', '1 minute');
select * from generate_series('2008-01-01 00:00'::timestamp,'2008-01-01 12:00', '1 hour');
select * from generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 day');
select * from generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 week');
select * from generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 month');
select * from generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 year');
select * from generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 decade');
select * from generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 century');
select * from generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 millennium');
If quarter cannot be used then what is the best other option? For now, I have:
select
date_trunc('quarter',generate_series) gs
from
generate_series('2008-01-01 00:00'::timestamp,'2009-01-01 12:00', '1 month')
group by
gs
order by
gs;

Shouldn't this do what you expect:
SELECT * FROM generate_series('2008-01-01 00:00'::timestamp,
'2009-01-01 12:00', '3 months');
Result:
generate_series
---------------------
2008-01-01 00:00:00
2008-04-01 00:00:00
2008-07-01 00:00:00
2008-10-01 00:00:00
2009-01-01 00:00:00
(5 rows)

Related

PostgreSQL - Add days to current_data with condition

I'm trying to add a day to current_date in between only when from hour is bigger then to hour
can any one help?
see code:
AND EXISTS(SELECT *
FROM jsonb_array_elements((${hours})::jsonb) hours(e)
WHERE hours.e->'active' = 'true'
AND now() BETWEEN
current_date +
(LEFT(hours.e->>'from', 2))::INTEGER * INTERVAL '1 HOUR' +
(RIGHT(hours.e->>'from', 2))::INTEGER * INTERVAL '1 MINUTE' AND
current_date +
// add day if from is bigger then to
(LEFT(hours.e->>'to', 2))::INTEGER * INTERVAL '1 HOUR' +
(RIGHT(hours.e->>'to', 2))::INTEGER * INTERVAL '1 MINUTE' )
a

How to select month end data in PostgreSQL?

I'm working on the Accrual Reversal query in PostgreSQL. The system running doesn't have the reversal flag. So I need to consider all the end of the day of previous month accrued invoices as the reversal amount. And need to union them all with the main query. I can do it for last month but invoice date are dynamic, user may give 2 years as invoice period. For those 2 years, all the previous month data should be considered as accrued reversal. Here is the query
select invoicename, * from accountpay where invoice_date between '2020-01-01' and '2021-12-31'
union all
select concat('Accured Reversal', invoicename) as reference, * from accountpay where accrual = true and invoice_date::date = (select concat(date_part('year',((('2021-12-30'::date) - interval '1 month'))), '-', date_part('month',((('2021-12-30'::date) - interval '1 month'))), '-01')::date + interval '1 month' - interval '1 day')
Please help me to do this.
Thanks in Advance
SELECT (
Date_trunc('MONTH',a) + interval '1 month -1 day ')
as last_day_of_month
FROM generate_series(
'2020-01-01 00:00'::timestamp
- interval '12 months',
'2022-01-01 00:00',
'1 month') as dt(a);
get last_day_of_month from '2020-01-01 00:00' till '2022-01-01 00:00'
Then your sql would be
invoice_date in
(SELECT (Date_trunc('MONTH',a) + interval '1 month -1 day ')
as last_day_of_month
FROM generate_series(
'2020-01-01 00:00'::timestamp
- interval '12 months',
'2021-01-01 00:00',
'1 month') as dt(a))
This will get the last day of last 12 months so the number of months will be place holder (dynamic) and all the last day of the months will be in IN clause.
SQL re-written:
WITH date_cte AS
(
SELECT Date_trunc('MONTH',dt)+ interval '1 month -1 day ' last_day_of_month
FROM generate_series('2021-11-30 00:00:00'::timestamp - interval '12 months','2021-11-30 00:00:00','1 month') t(dt))
select invoicename, * from accountpay where invoice_date between '2020-01-01' and '2021-12-31'
union all
select concat('Accured Reversal', invoicename) as reference, * from accountpay where accrual = true and invoice_date::date in (select * from date_cte);
Basically , the last dates are generated this way for 12 months:
WITH date_cte AS
(
SELECT Date_trunc('MONTH',dt)+ interval '1 month -1 day ' last_day_of_month
FROM generate_series('2021-11-30 00:00:00'::timestamp - interval '12 months','2021-11-30 00:00:00','1 month') t(dt))
SELECT *
FROM date_cte;
last_day_of_month
---------------------
2020-11-30 00:00:00
2020-12-31 00:00:00
2021-01-31 00:00:00
2021-02-28 00:00:00
2021-03-31 00:00:00
2021-04-30 00:00:00
2021-05-31 00:00:00
2021-06-30 00:00:00
2021-07-31 00:00:00
2021-08-31 00:00:00
2021-09-30 00:00:00
2021-10-31 00:00:00
2021-11-30 00:00:00
You can replace 12 months by any number of months or you can make it year too like:
...generate_series('2021-11-30 00:00:00'::timestamp - interval '1 year','2021-11-30 00:00:00','1 month')

How to get the 1st 6months i.e.(1-26weeks) and last 6months (26-52 weeks) from last year & also get last 5weeks from current date using postgresql

How to get the 1st 6months i.e.(1-26weeks) and last 6months (26-52 weeks) from last year & also how to get last 5weeks from current date using postgresql.
Like the below table structure
Id Title Description current_week_number current_year
-----------------------------------------------------
123 abc descr 48 2021
456 def descr1 45 2020
Based on the week number and year I'm trying to fetch the data.
can anyone help on this?
Thanks
Query the table on the last 6 months until now :
SELECT *
FROM your_table
WHERE (current_year || '0101') :: date + interval '7 days' * current_week >= Now() - interval '6 months'
AND (current_year || '0101') :: date + interval '7 days' * current_week <= Now()
Query the table between the last 12 months and the last 6 months from now :
SELECT *
FROM your_table
WHERE (current_year || '0101') :: date + interval '7 days' * current_week <= Now() - interval '6 months'
AND (current_year || '0101') :: date + interval '7 days' * current_week >= Now() - interval '12 months'
Query the table for the first 6 months of the current year :
SELECT *
FROM your_table
WHERE (current_year || '0101') :: date + interval '7 days' * current_week >= (extract(year from Now()) || '0101') :: date
AND (current_year || '0101') :: date + interval '7 days' * current_week <= (extract(year from Now()) || '0101') :: date + interval '6 months'
Query the table for the last 6 months of the current year :
SELECT *
FROM your_table
WHERE (current_year || '0101') :: date + interval '7 days' * current_week >= (extract(year from Now()) || '0101') :: date + interval '6 months'
AND (current_year || '0101') :: date + interval '7 days' * current_week <= (extract(year from Now()) || '0101') :: date + interval '12 months'
--records from 1st half of this year, based on week number
select Id, Title, Description, current_week_number, current_year
from your_table
where current_year=extract('year' from now())::int
and current_week_number<=26;
--records from 2nd half of this year, based on week number
select Id, Title, Description, current_week_number, current_year
from your_table
where current_year=extract('year' from now())::int
and current_week_number>26;
As to the original form of the question, getting the actual weeks:
using generate_series(date,date,interval) and extract('field' from date).
with first_26_weeks_of_last_year as
( select extract('week' from weeks) weeks
from generate_series(
make_date( extract('year' from 'today'::timestamp-'1 year'::interval)::int,
1,
1
),
make_date( extract('year' from 'today'::timestamp-'1 year'::interval)::int,
1,
1
)+'25 weeks'::interval,
'1 week'::interval) weeks),
last_6_months_of_last_year as
( select extract('week' from weeks) weeks
from generate_series(
make_date( extract('year' from 'today'::timestamp-'1 year'::interval)::int,
12,
31
),
make_date( extract('year' from 'today'::timestamp-'1 year'::interval)::int,
12,
31
)-'6 months'::interval,
'1 week'::interval) weeks),
five_weeks_from_this_week as
( select extract('week' from weeks) weeks
from generate_series(
'today'::date,
'today'::date+'4 weeks'::interval,
'1 week'::interval) weeks)
select 'first_26_weeks_of_last_year',a.weeks
from first_26_weeks_of_last_year a
union all
select 'last_6_months_of_last_year',a.weeks
from last_6_months_of_last_year a
union all
select 'five_weeks_from_this_week',a.weeks
from five_weeks_from_this_week a;

Speed up postgres read window query on overlapping date ranges

I have a table (simplified) that contains readings like follows
meter_id read_date value
1 2017-01-01 10
1 2017-01-15 15
1 2017-02-05 20
1 2017-04-15 22
2 2016-12-14 120
2 2016-03-02 200
This table contains millions of readings.
And I have a view (or query) that goes something like
select meter_id, read_date as start_read_date, value as start_value,
CASE
WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
ELSE lead(.read_date) OVER read_wdw::date
END::date AS read_end_date,
lead(value) OVER read_wdw AS end_value,
from reads_table
WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date);
I need to be able to query dates within a certain month. So start_read_date, end_read_date between e.g. '2017-01-01' and '2017-01-31'
So e.g.
select * from my_view where daterange(start_read_date,end_read_date, '[]') && daterange('2017-01-01', '2017-01-31', '[])
Which with the above table would return
meter_id start_read_date start_value end_read_date end_value
1 2017-01-01 10 2017-01-15 15
1 2017-01-15 15 2017-02-05 20
2 2016-12-14 120 2016-03-02 200
Is there a way to do a similar query on this table without having to build the whole view first to get my desired result?
Something like (which doesn't work)
select meter_id, read_date as start_read_date, value as start_value,
CASE
WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
ELSE lead(.read_date) OVER read_wdw::date
END::date AS read_end_date,
lead(value) OVER read_wdw AS end_value,
from reads_table
where read_date between '2017-01-01' and '2017-01-31'
or lead(read_date) over read_window between '2017-01-01' and '2017-01-31'
WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date);
Actually wrapping it in another select seems to resolve...
select * from (
select meter_id, read_date as start_read_date, value as start_value,
CASE
WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
ELSE lead(.read_date) OVER read_wdw::date
END::date AS read_end_date,
lead(value) OVER read_wdw AS end_value,
from reads_table
WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date)
)sub
where read_start_date between ...
or read_end_date between ...

How to get average between two dates in PostgreSql

I'd like to get the average date between two dates in Postgres:
In SqlServer is done with this simple query:
SELECT DateAdd(ms, DateDiff(ms, date_begin, date_end)/2, date_begin)
For example:
SELECT DateAdd(ms, DateDiff(ms, getdate(), dateadd(d, 1, getdate()))/2, getdate())
Now I'd like to make the same query in postgres.
I try with something like
SELECT current_timestamp + ((SELECT ((DATE_PART('day', (current_timestamp + INTERVAL '1 day')::timestamp - current_timestamp::timestamp) * 24 +
DATE_PART('hour', (current_timestamp + INTERVAL '1 day')::timestamp - current_timestamp::timestamp)) * 60 +
DATE_PART('minute', (current_timestamp + INTERVAL '1 day')::timestamp - current_timestamp::timestamp)) * 60 +
DATE_PART('second', (current_timestamp + INTERVAL '1 day')::timestamp - current_timestamp::timestamp)||'second')::interval
but it does not work.
Is there a simpler way to make this simple operation in Postgres?
How about:
select date_begin + (date_end - date_begin)/2