Reputation: 987
I need to get last day of all previous months including current month, upto a specified month. For example, I need last days of september, aug, july, june, may, april, march, feb, jan, dec 2015 like so:
temptable_mytable:
last_day_of_month
-----------------
2016-09-30
2016-08-31
2016-07-31
2016-06-30
2016-05-31
2016-04-30
2016-03-31
2016-02-30
2016-01-31
2015-12-31
I need to specify the month and year to go back to - in above case it's December 2015, but it could also be September 2015 and such. Is there a way that I can do a loop and do this instead of having to calculate separately for each month end?
Upvotes: 0
Views: 1511
Reputation: 3701
declare @LASTMONTH date = '2018-10-01';
WITH MTHS AS (
SELECT dateadd(month,month(getdate()),dateadd(year,year(getdate()) - 1900, 0)) aday
UNION ALL
SELECT DATEADD(month,1,aday) from MTHS WHERE aday <= @LASTMONTH
),
LASTDAYS AS (SELECT DATEADD(day,-1,aday) finaldayofmonth from MTHS)
select * from LASTDAYS
Here is a version that goes forward or backwards as appropriate
declare @LASTMONTH date = '2013-10-01';
WITH DIF AS (SELECT CASE WHEN
YEAR(@LASTMONTH) * 12 + MONTH(@LASTMONTH)
>= YEAR(GETDATE()) * 12 + MONTH(getdate()) THEN 1 ELSE -1 END x),
MTHS AS (
SELECT dateadd(month,month(getdate()),dateadd(year,year(getdate()) - 1900, 0)) aday
UNION ALL
SELECT DATEADD(month,(SELECT X from dif),aday) from MTHS
WHERE month(aday) != month(dateadd(month,1,@LASTMONTH)) or YEAR(aday) != YEAR(dateadd(month,1,@LASTMONTH))
),
LASTDAYS AS (SELECT DATEADD(day,-1,aday) finaldayofmonth from MTHS)
select * from LASTDAYS order by finaldayofmonth
Upvotes: 2
Reputation: 3003
This was cribbed together quick based on a couple different SO answers for the parts:
DECLARE @startdate datetime, @enddate datetime
set @startdate = '2015-12-01'
set @enddate = getdate()
;WITH T(date)
AS
(
SELECT @startdate
UNION ALL
SELECT DateAdd(day,1,T.date) FROM T WHERE T.date < @enddate
)
SELECT DISTINCT
DATEADD(
day,
-1,
CAST(CAST(YEAR(date) AS varchar) + '-' + CAST(MONTH(date)AS varchar) + '-01' AS DATETIME))
FROM T OPTION (MAXRECURSION 32767);
Upvotes: 0
Reputation: 2651
Use a recursive CTE with the EOMONTH function.
DECLARE @startdate DATE = '2016-01-01'
;WITH CTE
AS
(
SELECT EOMONTH(GETDATE()) as 'Dates'
UNION ALL
SELECT EOMONTH(DATEADD(MONTH, -1, [Dates]))
FROM CTE WHERE Dates > DATEADD(MONTH, 1, @startdate)
)
SELECT * FROM CTE
Upvotes: 4
Reputation: 13173
This should scale out no matter how far you go back or forward for your originating table or object.
SET NOCOUNT ON;
DECLARE @Dates TABLE ( dt DATE)
DECLARE @Start DATE = DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)
DECLARE @End DATE = DATEADD(YEAR, 1, @Start)
WHILE @Start <= @End
BEGIN
INSERT INTO @Dates (dt) VALUES (@Start)
SELECT @Start = DATEADD(DAY, 1, @Start)
END
; With x as
(
Select
dt
, ROW_NUMBER() OVER(PARTITION BY DATEPART(YEAR, Dt), DATEPART(MONTH, Dt) ORDER BY Dt Desc) AS rwn
From @Dates
)
Select *
From x
WHERE rwn = 1
ORDER BY Dt
Upvotes: 1
Reputation: 421
with temp as (select -1 i union all
select i+1 i from temp where i < 8)
select DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+i*-1,0)) from temp
Upvotes: 3
Reputation: 147374
Here's one approach, using a CTE to generate a list of incrementing numbers to allow us to then have something to select from and use in a DATEADD to go back for the appropriate number of months.
Typically, if you're doing this quite frequently, instead of generating numbers on the fly like this with the CROSS JOIN, I'd recommend just creating a "Numbers" table that just holds numbers from 1 to "some number high enough to meet your needs"
DECLARE @Date DATE = '20151201'
DECLARE @MonthsBackToGo INTEGER
SELECT @MonthsBackToGo = DATEDIFF(mm, @Date, GETDATE()) + 1;
WITH _Numbers AS
(
SELECT TOP (@MonthsBackToGo) ROW_NUMBER() OVER (ORDER BY o.object_id) AS Number
FROM sys.objects o
CROSS JOIN sys.objects o2
)
SELECT EOMONTH(DATEADD(mm, -(Number- 1), GETDATE())) AS last_day_of_month
FROM _Numbers
Upvotes: 1