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')
Related
In PostgreSQL, the interval of '1 month' sometimes counts as 30 days and sometimes counts as 31 days. What are the criteria used to determine this?
I ran the below query to demonstrate my confusion.
select
now() - interval '1 month'
, now() - interval '30 days'
, interval '30 days' = interval '1 month'
, interval '31 days' = interval '1 month'
The query returns:
2022-03-27 21:09:30.933434+00 | 2022-03-28 21:09:30.933434+00 | true | false
I would expect the query to return both days on March 28th, since an interval of one month is equal to an interval of 30 days.
It comes down to the specific vs the general where day is the specific and month is not. The same happens with day and hour as in:
select '2022-03-13 12:00 PDT'::timestamptz - '1 day'::interval;
?column?
------------------------
2022-03-12 12:00:00-08
select '2022-03-13 12:00 PDT'::timestamptz - '24 hours'::interval;
?column?
------------------------
2022-03-12 11:00:00-08
DST occurred morning of 2022-03-13 in PST/PDT. So a day is generalized to the same time a day ago whereas 24 hours ago is actually 24 hours passing.
In your case:
select
now() - interval '1 month'
, now() - interval '30 days';
?column? | ?column?
-------------------------------+-------------------------------
2022-03-27 14:44:33.515669-07 | 2022-03-28 14:44:33.515669-07
The 1 month is going to go back to the same date and time one month back, whereas 30 days is going back an actual 30 days.
In this case:
select '2022-03-30 21:17:05'::timestamp - interval '1 month' ;
?column?
---------------------
2022-02-28 21:17:05
There is no day 30 in February so it goes to the actual end of the month the 28th.
SELECT to_char(date_trunc('day', (current_date - days)), 'YYYY-MM-DD')
AS date
FROM generate_series(0, 365, 1)
AS days
result
2021-11-12
2021-11-11
2021-11-10
2021-11-09
2021-11-08
2021-11-07 .....
2020-11-16
2020-11-15
2020-11-14
2020-11-13
2020-11-12 ..... end
How do I exclude weekend data from the year's worth of data?
SELECT to_char(running_day, 'YYYY-MM-DD') AS date
FROM (select current_date - generate_series(1, 365, 1)) AS t(running_day)
WHERE extract('isodow' from running_day) between 1 and 5
ORDER BY running_day;
Just add a WHERE clause:
SELECT to_char(day, 'YYYY-MM-DD') AS date
FROM generate_series(current_date - interval '1 year',
current_date,
interval '1 day') AS g(day)
where extract(isodow from g.day) not in (6,7)
I used the ISO definition of the day numbering with Monday = 1, Saturday = 6 and Sunday = 7.
You can use extract(dow ...) if you prefer Sunday = 0
I'm trying to find the date of the end of previous year in Postgres.
This will give me the first day of previous year
SELECT date_trunc('year', now()- interval '1 year')
returns the correct result #=> 2018-01-01 00:00:00
But trying to remove 1day from the first day of the current year doesn't give me the last day of previous year:
SELECT date_trunc('year', now() - interval '1 day')
returns #=> 2018-01-01 00:00:00
When I'm expecting it to be 2018-12-31
Still gives me the first day of the current year. Even if i remove 100 day it still returns the same result.
Same behavior for previous years:
SELECT date_trunc('year', now()- interval '2 year');
returns #=> 2017-01-01 00:00:00 which is what I expect.
but :
SELECT date_trunc('year', now()- interval '1 year' - interval '1 day');
returns #=> 2018-01-01 00:00:00 when I'm expecting 2017-12-31
--
For future reference I'm posting this in 2019.
This is an order of operations issue. Truncate the date before subtracting:
SELECT date_trunc('year', now()) - interval '1 day';
SELECT date_trunc('year', now()- interval '1 year') - interval '1 day';
Disclosure: I work for EnterpriseDB (EDB)
I also have the question how do i get code block to work on stack overflow but that's a side issue.
I have this quasi-code that works:
select
*
from
unnest('{2018-6-1,2018-7-1,2018-8-1,2018-9-1}'::date[],
'{2018-6-30,2018-7-31,2018-8-31,2018-9-30}'::date[]
) zdate(start_date, end_date)
left join lateral pipe_f(zdate...
But now I want it to work from 6/1/2018 until now(). What's the best way to do this.
Oh, postgresql 10. yay!!
Your query gives a list of first and last days of months between "2018-06-01" and now. So I am assuming that you want to this in a more dynamic way:
demo: db<>fiddle
SELECT
start_date,
(start_date + interval '1 month -1 day')::date as end_date
FROM (
SELECT generate_series('2018-6-1', now(), interval '1 month')::date as start_date
)s
Result:
start_date end_date
2018-06-01 2018-06-30
2018-07-01 2018-07-31
2018-08-01 2018-08-31
2018-09-01 2018-09-30
2018-10-01 2018-10-31
generate_series(timestamp, timestamp, interval) generates a list of timestamps. Starting with "2018-06-01" until now() with the 1 month interval gives this:
start_date
2018-06-01 00:00:00+01
2018-07-01 00:00:00+01
2018-08-01 00:00:00+01
2018-09-01 00:00:00+01
2018-10-01 00:00:00+01
These timestamps are converted into dates with ::date cast.
Then I add 1 month to get the next month. But as we are interested in the last day of the previous month I subtract one day again (+ interval '1 month -1 day')
Another option that's more ANSI-compliant is to use a recursive CTE:
WITH RECURSIVE
dates(d) AS
(
SELECT '2018-06-01'::TIMESTAMP
UNION ALL
SELECT d + INTERVAL '1 month'
FROM dates
WHERE d + INTERVAL '1 month' <= '2018-10-01'
)
SELECT
d AS start_date,
-- add 1 month, then subtract 1 day, to get end of current month
(d + interval '1 month') - interval '1 day' AS end_date
FROM dates
Have two dates - '2018-05-01' and '2018-06-01'. I would like to expand this window to the past by day difference of those dates.
SELECT * FROM data
WHERE
start_time > CAST('2018-05-01' AS timestamptz) - INTERVAL '30 DAY'
AND start_time < CAST('2018-06-01' AS timestamptz)
How can I replace INTERVAL '30 DAY' with number of days between given dates without explicitly defining number of days? I know to calculate day difference:
date_part('day',age('2018-05-01', '2018-06-01'))
But not sure how to incorporate into the substraction. Dates and days between them will change.
You can use date_trunc('mon', some_date_expression) to round down to the start of a month:
select date_trunc('mon', now() - '3 mon'::interval) as date_begin
, date_trunc('mon', now() - '1 day'::interval) as date_end
;
Result
date_begin | date_end
------------------------+------------------------
2018-03-01 00:00:00+01 | 2018-06-01 00:00:00+02
(1 row)
You can simply subtract the difference from the start date:
with t (start_date, end_date) as (
values (date '2018-05-01', date '2018-06-01')
)
select start_date - (end_date - start_date) as new_start,
end_date
from t;
returns
new_start | new_end
-----------+-----------
2018-03-31 | 2018-06-01