I have an oracle query to change it into postgres
SELECT cast(to_char(ADD_MONTHS(TRUNC(ADD_MONTHS(SYSDATE, -6),'MM'),LEVEL - 1),'MMYYYY') as number) monthid,
to_char (ADD_MONTHS(TRUNC(ADD_MONTHS(SYSDATE, -6),'MM'), LEVEL - 1),'MON-YYYY') monthdesc
From dual
CONNECT BY LEVEL <= MONTHS_ BETWEEN (SYSDATE, ADD_MONTHS (SYSDATE, -6)) + 1;
I tried with CTE and generate_series, but stuck somewhere to get result set
---------------------
MONTHID MONTHDESC
---------------------
172022 JUL-2022
82022 AUG-2022
92022 SEP-2022
102022 OCT-2022
112022 NOV-2022
122022 DEC-2022
12023 JAN-2023
This will generate a list of the last six months:
select to_char(g.dt, 'mmyyyy') as monthid,
to_char(g.dt, 'MON-yyyy') as monthdesc
from generate_series(date_trunc('month', current_date) - interval '6 month',
date_trunc('month', current_date), interval '1 month') as g(dt)
However this will return 072022 for July 2022, not 172022 as in your sample data.
Related
When I set up a date range using max(lasttime) for the upper bound, the query works.
range_values as (
select date_trunc('month', current_date) as minval,
max(lasttime) as maxval
from people
)
When I use date_trunc('day', current_date) + interval '1 day' - interval '1 second' for the upper bound, the query hangs seemingly forever.
range_values as (
select date_trunc('month', current_date) as minval,
(
date_trunc('day', current_date) + interval '1 day' - interval '1 second'
) as maxval
from people
)
Here's how those values differ.
select max(lasttime) as max_lasttime, (date_trunc('day', current_date) + interval '1 day' - interval '1 second') as end_of_day from people;
{
"max_lasttime": "2023-02-13 07:30:01",
"end_of_day": "2023-02-13 23:59:59-07"
}
I expected this would not make a difference. Why does it?
PostgreSQL 10.18 (Ubuntu 10.18-0ubuntu0.18.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0, 64-bit
In the 1st query, you are using an aggregate (max) so the output is a single row, regardless of the size of the people table.
In the 2nd query, you are fetching 2 constant values for every row in the people, so chances are you are building a massive cross-join with another (or the same!) table.
I would like to print this table (displaying only 4 rows for brevity):
Dates
Period
01-MAR-2022
61
02-MAR-2022
61
03-MAR-2022
61
30-APR-2022
61
So far I have:
SELECT CAST(TRUNC(date_trunc('month',CURRENT_DATE) + interval '-2 month') AS DATE) + (n || 'day')::INTERVAL AS Dates
, date_trunc('month',CURRENT_DATE) + interval '-2 month' + INTERVAL '2 month' - date_trunc('month',CURRENT_DATE) + interval '-2 month' AS Period
FROM generate_series(0,61) n
Please help with a better way of generating the period and also replacing the hard-coded 61 in generate_series(0,61).
Thanks!
What are you actually trying to accomplish, it is not clear nor specified. BTW your query is invalid. It appears you looking to list each data from first date of 2 months prior to the last date of 1 month prior and the total number of days in that range. The following would give the first date, and using date subtraction gives the number of days.
with full_range( first_dt, num_days) as
( select date_trunc ('month', (current_date - interval '2 months'))::date
, date_trunc ('month', (current_date - interval '1 day'))::date -
date_trunc ('month', (current_date - interval '2 months'))::date
)
select *
from full_range;
With that in hand you can use the num_days with generate series with the expression
select generate_series(0, num_days-1) from full_range
Finally combine the above arriving at: (see demo)
with full_range( first_dt, num_days) as
( select date_trunc ('month', (current_date - interval '2 months'))::date
, date_trunc ('month', (current_date - interval '1 day'))::date -
date_trunc ('month', (current_date - interval '2 months'))::date
)
select (first_dt + n*interval '1 day')::date, num_days
from full_range
cross join (select generate_series(0, num_days-1) from full_range) gn(n);
Is there any way to get the first day of previous month other than
date_trunc('month', current_date - interval '1 month') ?
I'm trying to save a query in a Report Designer software (DBxtra) but the software freezes while using the "interval" feature of PostgreSQL.
You can try to calculate the previous month manually.
One approach is extract the month and subtract 1 if it is not 12(in this case you return 1):
SELECT to_timestamp(concat(EXTRACT(YEAR from current_date), '-',CASE (EXTRACT(MONTH from current_date)) WHEN 12 THEN 1 ELSE (EXTRACT(MONTH from current_date)-1) END,'-', 1), 'YYYY-MM-DD');
If you need it without timezone:
SELECT to_timestamp(concat(EXTRACT(YEAR from current_date), '-',CASE (EXTRACT(MONTH from current_date)) WHEN 12 THEN 1 ELSE (EXTRACT(MONTH from current_date)-1) END,'-', 1), 'YYYY-MM-DD')::timestamp without time zone;
Try:
make_interval(month := 1)
'1 month'::interval
cast('1 month' as interval)
instead of interval '1 month'
I'm trying to compare values of current month's data to previous months using PostgreSQL. So if today is 4/23/2018, I want the data for 3/23/2018.
I've tried current_date - interval '1 month' but it is problematic for months with 31 days.
My table is structured as simply as
date, value
Check this example query:
WITH dates AS (SELECT date::date FROM generate_series('2018-01-01'::date, '2018-12-31'::date, INTERVAL '1 day') AS date)
SELECT
start_dates.date AS start_date,
end_dates.date AS end_date
FROM
dates AS start_dates
RIGHT JOIN dates AS end_dates
ON ( start_dates.date + interval '1 month' = end_dates.date AND
end_dates.date - interval '1 month' = start_dates.date);
It will output all end_dates and corresponding start_dates. The corresponding dates are defined by interval '1 month' and checked in both ways:
start_dates.date + interval '1 month' = end_dates.date AND
end_dates.date - interval '1 month' = start_dates.date
The output looks like this:
....
2018-02-26 2018-03-26
2018-02-27 2018-03-27
2018-02-28 2018-03-28
2018-03-29
2018-03-30
2018-03-31
2018-03-01 2018-04-01
2018-03-02 2018-04-02
2018-03-03 2018-04-03
2018-03-04 2018-04-04
....
Note, that there are 'gaps' for days without corresponding dates.
Back to your table, join the table with itself (giving aliases) and use given join condition, so the query would look like this:
SELECT
start_dates.value - end_dates.value AS change,
start_dates.date AS start_date,
end_dates.date AS end_date
FROM
_your_table_name_ AS start_dates
RIGHT JOIN _your_table_name_ AS end_dates
ON ( start_dates.date + interval '1 month' = end_dates.date AND
end_dates.date - interval '1 month' = start_dates.date);
Given the following table structure:
create table t (
d date,
v int
);
After populating with some dates and values, there is a way to find the value of the previous month using simple calculations and the LAG function, without resorting to joins. I am not sure how it compares from a performance perspective, so please run your own tests before selecting which solution to use.
select
*,
lag(v, day_of_month) over (order by d) as v_end_of_last_month,
lag(v, last_day_of_previous_month + day_of_month - cast(extract(day from d - interval '1 month') as int)) over (order by d) as v_same_day_last_month
from (
select
*,
lag(day_of_month, day_of_month) over (order by d) as last_day_of_previous_month
from (
select
*,
cast(extract(day from d) as int) as day_of_month
from
t
) t_dom
) t_dom_ldopm;
You may note that between the 29th and 31st of March, the comparison will be made against the 28th of February, since the same day does not exist in February for those particular dates. The same logic applies to other months with different number of days.
How to get last 3 months records from the table.
SELECT *
from table
where month > CURRENT_DATE-120
and month < CURRENT_DATE
order by month;
I have used the above query is it correct? shall I use this for get last 3 month record from the table.
You can use built-in INTERVAL instruction
Check how this works:
SELECT CURRENT_DATE - INTERVAL '3 months'
and you can rewrite your SQL to:
SELECT * from table where date > CURRENT_DATE - INTERVAL '3 months'
(not checked but this should give you an idea how to use INTERVAL instruction)
Try that:
SELECT *
FROM table
WHERE month BETWEEN EXTRACT(MONTH FROM NOW() - INTERVAL '3 months')
AND EXTRACT(MONTH FROM NOW())
ORDER BY month
;
This filters the last 3 calendar months
SELECT * from table where date >= to_char(CURRENT_DATE - INTERVAL '3 months', 'YYYY-MM-01')::date
select date::date
from generate_series((current_date - INTERVAL '1 Month')::date, (current_date - INTERVAL '1 DAY')::date,'1
day'::interval) date
WHERE date >= date_trunc('month', current_date - interval '3' month)
and date < date_trunc('month', current_date)
This will give last three months date list, excluding current months date. Example if current month is November. This list will give use all dates of August, Septemeber and October.