PostgreSQL Generate Series that displays rolling trailing 12 months - sql

I'm attempting to create a Generate Series that displays the trailing 12 months based on the current date. Currently, I'm just getting the current month displayed 12 times.
select date_trunc('month',current_date)::date as month
from generate_series(
date_trunc('month', current_date)::date - interval '11 months',current_date, '1 month')
Any help would be greatly appreciated.

Your select overrides the series returned. So all you get is 12 instances (the number of rows returned from the series) of date_trunc('month',current_date)::date, which is always the current month of course.
You probably want this:
select *
from generate_series(
date_trunc('month', current_date)::date - interval '11 months',current_date, '1 month')
Or with an alias:
select dMonths
from generate_series(
date_trunc('month', current_date)::date - interval '11 months',current_date, '1 month') dMonths

Related

Get the timestamp of the last and first day of a month in postgresSQL

I am currently trying to figure out the equivalent functions in PostgresSQL for the below MariaDB functions:
SELECT
CONCAT(DATE_ADD(DATE_ADD(LAST_DAY('2021-02-15 00:00:00'),INTERVAL 1 DAY),INTERVAL -1 MONTH), ' ', '00:00:00') AS first_day_of_the_month,
CONCAT(LAST_DAY('2021-02-15 23:59:59'), ' ' ,'23:59:59') AS last_day_of_the_month;
Expected Result:
first_day_of_the_month | last_day_of_the_month
------------------------|-------------------------------
2021-02-01 00:00:00 | 2021-02-28 23:59:59
I want to get the last day and the first day of the month of a certain date including the time 00:00:00 for the first day and 23:549:59 for the last day.
So far I am comming close to the expected result with these queries:
SELECT
(date_trunc('MONTH', '2021-02-15 00:00:00'::TIMESTAMP) + INTERVAL '1 MONTH - 1 day')::TIMESTAMP
+ '1 days'::INTERVAL - '1 months'::INTERVAL AS first_day_of_the_month,
(date_trunc('MONTH', '2021-02-15 23:59:59'::TIMESTAMP) + INTERVAL '1 MONTH - 1 day')::TIMESTAMP AS last_day_of_the_month;
However, as you can see in the DB-Fiddle for the last_day_of_the_month I am getting 00:00:00 instead of 23:59:59.
How do I need to change the query to get the correct timestamp?
demo:db<>fiddle
/* Option 1: Certain Date */
SELECT
date_trunc('month', timestamp '2021-02-15 00:00:00') AS first_timestamp,
date_trunc('month', timestamp '2021-02-15 23:59:59') + interval '1 month - 1 second' AS last_timestamp;
/* Option 2: Current_date */
SELECT
date_trunc('month', current_date)::timestamp AS first_timestamp,
date_trunc('month', current_date)::timestamp + interval '1 month - 1 second' AS last_timestamp;
First timestamp of month
date_trunc('month', ...) normalizes the date to the first possible timestamp of the month. So it returns 00:00:00 of the first day of the month
Last timestamp of month
If you add a month to the previous result, you get the first day/timestamp (00:00:00) of the next month. If you subtract a day from this, you get the last day of the current month. Of course, you can subtract just a second to get 23:59:59 of the last day of the month as you expected.
For the first day of the month:
date_trunc('month', the_date_column)
For the last day:
date_trunc('month', the_date_column) + interval '1 month' - interval '1 day'
Or if you prefer:
date_trunc('month', date) + interval '1 month - 1 day'
If you really one one second from midnight on the last day, replace day with second in the logic. I don't recommend that. In fact, if you want to define a range of date, define an open range and just use the first day of the next month. Then you can learn about tsrange which builds this into the language.

SQL Query to get records from last week of every month for 4 years

I have below table
ABC Date
200 2019-02-22
-200 2019-02-23
1200 2019-02-24
-500 2019-02-25
'
'
'
'
-889 2015-01-11
I need to get values for from ABC for every day of last week of every month
select ABC
from table 1
where date between '2019-03-26' and '2019-03-30'
this is for month of march 2019. How do i create a loop such that it displays value for everyday of last week of every month for 3 years
You can use date arithmetic to get the last week of each month. In Terdata, I think this is one solution:
select abc
from table1
where (date >= (current_date - extract(day from date) * interval '1 day') - interval '6 day' and
date <= current_date - extract(day from date) * interval '1 day'
) or
(date >= (current_date - extract(day from date) * interval '1 day') - interval '1 month' - interval '6 day' and
date <= current_date - extract(day from date) * interval '1 day' - interval '1 month'
) or
(date >= (current_date - extract(day from date) * interval '1 day') - interval '2 month' - interval '6 day' and
date <= current_date - extract(day from date) * interval '1 day' - interval '12month'
);
SELECT ABC, DATE FROM table_1 WHERE DATEPART(wk, DATE) =
DATEPART(wk, EOMONTH(DATE)) AND DATE <= DATEADD(year,3,GETDATE())
DATEPART(wk, DATE) gives me the week number of that date, DATEPART(wk,EOMONTH(DATE)) gives me the week number of (the last day of the corresponding date's month). So, when I check this, I will only select dates belonging to the last week of every month. The next filter is to select only those dates which are lesser than 3 years from now (GETDATE()).

How to get number of days in month based on date?

Is there a way to use extract from date in format YYYY-MM-DD how many days were in this month?
example:
for 2016-02-05 it will give 29 (Feb 2016 has 29 days)
for 2016-03-12 it will give 31
for 2015-02-05 it will give 28 (Feb 2015 had 28 days)
I'm using PostgreSQL
EDIT:
LAST_DAY function in postgres is not what i'm looking for. it returns DATE while I expect an Integer
One way to achieve this would be to subtract the beginning of the following month from the beginning of the current month:
db=> SELECT DATE_TRUNC('MONTH', '2016-02-05'::DATE + INTERVAL '1 MONTH') -
DATE_TRUNC('MONTH', '2016-02-05'::DATE);
?column?
----------
29 days
(1 row)
Just needed this today and seems that I came up with pretty much the same as Mureinik, just that I needed it numeric. (PostgreSQL couldn't convert from interval to number directly)
previous month:
select CAST(to_char(date_trunc('month', current_date) - (date_trunc('month', current_date) - interval '1 month'),'dd') as integer)
current month:
select CAST(to_char(date_trunc('month', current_date) + interval '1 month' - date_trunc('month', current_date), 'dd') as integer)
You can try next:
SELECT
DATE_PART('days',
DATE_TRUNC('month', TO_DATE('2016-02-05', 'YYYY-MM-DD'))
+ '1 MONTH'::INTERVAL
- DATE_TRUNC('month', TO_DATE('2016-02-05', 'YYYY-MM-DD'))
);
Note: there date is used twice. And used convert function TO_DATE

postgres query to check for records not falling between two time periods using the ISO WEEK number and year from DB

i do have a query which works fine but I was just wondering if there are other ways or alternate method to bettter this.
I have a table where i am fetching those records exceeding or do not fall between 1 year time interval however there is only the year and ISO week number column in the table (integer values).
basically the logic is to check ISO WEEK - YEAR falls between 'current_date - interval '1 year' AND current_date.
My query is as below :
select * from raj_weekly_records where
(date_dimension_week > extract(week from current_date) and date_dimension_year = extract(year from current_date) )
or (date_dimension_week < extract(week from current_date) and (extract(year from current_date)-date_dimension_year=1) )
or(extract(year from current_date)-date_dimension_year>1);
Here date_dimension_week and date_dimension_year are the only integer parameters by which I need to check is there any other alternate or better way?.This code is working fine no issues here.
Here is an idea. Convert the year/week to a numeric format: YYYYWW. That is, the year times 100 plus the week number. Then you can do the logic with a single comparison:
select *
from raj_weekly_records
where date_dimension_year * 100 + date_dimension_week
not between (extract(year from current_date) - 1) * 100 + extract(week from current_date) and
extract(year from current_date) * 100 + extract(week from current_date)
(There might be an off-by one error, depending on whether the weeks at the ends are included or excluded.)
select *
from raj_weekly_records
where
date_trunc('week',
'0001-01-01 BC'::date + date_dimension_year * interval '1 year'
)
+ (date_dimension_week + 1) * interval '1 week'
- interval '1 day'
not between
current_date - interval '1 year' and current_date

Generate series of week intervals for given month

In a Postgres 9.1 database, I am trying to generate a series of weeks for a given month but with some constraints. I need all weeks to start on Monday and get cut when they start or end in another month.
Example:
For February, 2013 I want to generate a series like this:
start
------------------------
2013-02-01 00:00:00+00
2013-02-04 00:00:00+00
2013-02-11 00:00:00+00
2013-02-18 00:00:00+00
2013-02-25 00:00:00+00
The query that I have now looks like this:
SELECT GREATEST(date_trunc('week', dates.d),
date_trunc('month',dates.d)) as start
FROM generate_series(to_timestamp(1359676800),to_timestamp(1362095999), '1 week') as dates(d)
This query gets me the first 4 weeks but it's missing the week from the 25th. Is it possible to get the last week?
SELECT generate_series(date_trunc('week', date '2013-02-01' + interval '6 days')
, date_trunc('week', date '2013-02-01' + interval '1 month - 1 day')
, interval '1 week')::date AS day
UNION SELECT date '2013-02-01'
ORDER BY 1;
This variant does not need a subselect, GREATEST or GROUP BY and only generates the required rows. Simpler, faster. It's cheaper to UNION one row.
Add 6 days to the first day of the month before date_trunc('week', ...) to compute the first Monday of the month.
Add 1 month and subtract 1 day before date_trunc('week', ...) to get the last Monday of the month.
This can conveniently be stuffed into a single interval expression: '1 month - 1 day'
UNION (not UNION ALL) the first day of the month to add it unless it's already included as Monday.
Note that date + interval results in timestamp, which is the optimum here. Detailed explanation:
Generating time series between two dates in PostgreSQL
Automation
You can provide the start of the date series in a CTE:
WITH t(d) AS (SELECT date '2013-02-01') -- enter 1st of month once
SELECT generate_series(date_trunc('week', d + interval '6 days')
, date_trunc('week', d + interval '1 month - 1 day')
, interval '1 week')::date AS day
FROM t
UNION SELECT d FROM t
ORDER BY 1;
Or wrap it into a simple SQL function for convenience with repeated calls:
CREATE OR REPLACE FUNCTION f_week_starts_this_month(date)
RETURNS SETOF date AS
$func$
SELECT generate_series(date_trunc('week', $1 + interval '6 days')
, date_trunc('week', $1 + interval '1 month - 1 day')
, interval '1 week')::date AS day
UNION
SELECT $1
ORDER BY 1
$func$ LANGUAGE sql IMMUTABLE;
Call:
SELECT * FROM f_week_starts_this_month('2013-02-01');
You would pass the date for the first day of the month, but it works for any date. You the first day and all Mondays for the following month.
select
greatest(date_trunc('week', dates.d), date_trunc('month',dates.d)) as start
from generate_series('2013-02-01'::date, '2013-02-28', '1 day') as dates(d)
group by 1
order by 1