WingMan20-10
WingMan20-10

Reputation: 3734

t-sql select get all Months within a range of years

I need a select to return Month and year Within a specified date range where I would input the start year and month and the select would return month and year from the date I input till today.

I know I can do this in a loop but I was wondering if it is possible to do this in a series selects?

Year  Month
----  -----
2010  1
2010  2
2010  3
2010  4
2010  5
2010  6
2010  7

and so on.

Upvotes: 7

Views: 22373

Answers (8)

Aaron Friel
Aaron Friel

Reputation: 1095

You can use something like this: Link

To generate the equivalent of a numbers table using date ranges.

But could you please clarify your inputs and outputs?

Do you want to input a start date, for example, '2010-5-1' and end date, for example, '2010-8-1' and have it return every month between the two? Do you want to include the start month and end month, or exclude them?

Here's some code that I wrote that will quickly generate an inclusive result of every month between two dates.

--Inputs here:
DECLARE @StartDate datetime;
DECLARE @EndDate datetime;
SET @StartDate = '2010-1-5 5:00PM';
SET @EndDate = GETDATE();

--Procedure here:
  WITH RecursiveRowGenerator (Row#, Iteration) AS (
       SELECT 1, 1
        UNION ALL
       SELECT Row# + Iteration, Iteration * 2
         FROM RecursiveRowGenerator
        WHERE Iteration * 2 < CEILING(SQRT(DATEDIFF(MONTH, @StartDate, @EndDate)+1))
        UNION ALL
       SELECT Row# + (Iteration * 2), Iteration * 2
         FROM RecursiveRowGenerator
        WHERE Iteration * 2 < CEILING(SQRT(DATEDIFF(MONTH, @StartDate, @EndDate)+1))
     )
     , SqrtNRows AS (
       SELECT *
         FROM RecursiveRowGenerator
        UNION ALL
       SELECT 0, 0
     )
SELECT TOP(DATEDIFF(MONTH, @StartDate, @EndDate)+1) 
       DATEADD(month, DATEDIFF(month, 0, @StartDate) + A.Row# * POWER(2,CEILING(LOG(SQRT(DATEDIFF(MONTH, @StartDate, @EndDate)+1))/LOG(2))) + B.Row#, 0)  Row#
  FROM SqrtNRows A, SqrtNRows B
 ORDER BY A.Row#, B.Row#;

Upvotes: 2

bsplosion
bsplosion

Reputation: 2866

I know this is an old question, but I'm mildly horrified at the complexity of some of the answers. Using a CTE is definitely the simplest way to go for selecting these values:

WITH months(dt) AS 
   (SELECT getdate() dt 
    UNION ALL
    SELECT dateadd(month, -1, dt)
    FROM months)
SELECT 
top (datediff(month, '2017-07-01' /* start date */, getdate()) + 1) 
YEAR(months.dt) yr, MONTH(months.dt) mnth
FROM months
OPTION (maxrecursion 0);

Just slap in whichever start date you'd like in place of the '2017-07-01' above and you're good to go with an efficient and easily-integrated solution.


Edit: Jeff Moden's answer quite effectively advocates against using rCTEs. However, in this case it appears to be a case of premature optimization - we're talking about 10's of records in all likelihood, and even if you span back to 1900 from today, it's still a minuscule hit. Using rCTEs to achieve code maintainability seems to be worth the trade if the expected result set is small.

Upvotes: 2

Andrey Morozov
Andrey Morozov

Reputation: 7969

Code below generates the values for the range between 21 Jul 2013 and 15 Jan 2014. I usually use it in SSRS reports for generating lookup values for the Month parameter.

declare
    @from date = '20130721',
    @to date = '20140115';

with m as (
select * from (values ('Jan', '01'), ('Feb', '02'),('Mar', '03'),('Apr', '04'),('May', '05'),('Jun', '06'),('Jul', '07'),('Aug', '08'),('Sep', '09'),('Oct', '10'),('Nov', '11'),('Dec', '12')) as t(v, c)),

y as (select cast(YEAR(getdate()) as nvarchar(4)) [v] union all select cast(YEAR(getdate())-1 as nvarchar(4)))

select m.v + ' ' + y.v [value_field], y.v + m.c [label_field]
from m
cross join y
where y.v + m.c between left(convert(nvarchar, @from, 112),6) and left(convert(nvarchar, @to, 112),6)
order by y.v + m.c desc

Results:

value_field     label_field
---------------------------
Jan 2014        201401
Dec 2013        201312
Nov 2013        201311
Oct 2013        201310
Sep 2013        201309
Aug 2013        201308
Jul 2013        201307

Upvotes: 1

Siju Dasan
Siju Dasan

Reputation: 9

DECLARE @Date1 DATE
DECLARE @Date2 DATE

SET @Date1 = '20130401'
SET @Date2 = DATEADD(MONTH, 83, @Date1)

SELECT DATENAME(MONTH, @Date1) "Month", MONTH(@Date1) "Month Number", YEAR(@Date1) "Year"
INTO #Month

WHILE (@Date1 < @Date2)
BEGIN 
    SET @Date1 = DATEADD(MONTH, 1, @Date1)
    INSERT INTO #Month
    SELECT DATENAME(MONTH, @Date1) "Month", MONTH(@Date1) "Month Number", YEAR(@Date1) "Year"
END

SELECT * FROM #Month 
ORDER BY [Year], [Month Number]

DROP TABLE #Month

Upvotes: 0

galih
galih

Reputation: 14

declare @date1 datetime, 
    @date2 datetime, 
    @date  datetime, 
    @month integer, 
    @nm_bulan varchar(20) 

create table #month_tmp 
    ( bulan integer null, keterangan varchar(20) null ) 

select @date1 = '2000-01-01', 
       @date2 = '2000-12-31' 

select @month = month(@date1) 

while (@month < 13) 
Begin 
    IF @month = 1 
    Begin 
       SELECT @date  = CAST( CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,0,@date1))-1),DATEADD(mm,0,@date1)),111) + ' 00:00:00' as DATETIME ) 
    End
    ELSE
    Begin
       SELECT @date  = CAST( CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,@month -1,@date1))-1),DATEADD(mm,@month -1,@date1)),111) + ' 00:00:00' as DATETIME ) 
    End
    select @nm_bulan = DATENAME(MM, @date)

    insert into #month_tmp
    select @month as nilai, @nm_bulan as nama 

    select @month = @month + 1
End 

select * from #month_tmp 
drop table #month_tmp 
go

Upvotes: -1

Jeff Moden
Jeff Moden

Reputation: 3494

Gosh folks... using a "counting recursive CTE" or "rCTE" is as bad or worse than using a loop. Please see the following article for why I say that.

http://www.sqlservercentral.com/articles/T-SQL/74118/

Here's one way to do it without any RBAR including the "hidden RBAR" of a counting rCTE.

--===== Declare and preset some obviously named variables
DECLARE @StartDate DATETIME,
        @EndDate   DATETIME
;
 SELECT @StartDate = '2010-01-14', --We'll get the month for both of these 
        @EndDate   = '2020-12-05'  --dates and everything in between
;
WITH
cteDates AS
(--==== Creates a "Tally Table" structure for months to add to start date
     -- calulated by the difference in months between the start and end date.
     -- Then adds those numbers to the start of the month of the start date.
 SELECT TOP (DATEDIFF(mm,@StartDate,@EndDate) + 1)
        MonthDate = DATEADD(mm,DATEDIFF(mm,0,@StartDate) 
                  + (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1),0)
   FROM sys.all_columns ac1
  CROSS JOIN sys.all_columns ac2
)
--===== Slice each "whole month" date into the desired display values.
 SELECT [Year]  = YEAR(MonthDate),
        [Month] = MONTH(MonthDate) 
   FROM cteDates
;

Upvotes: 17

Richard Davison
Richard Davison

Reputation: 9

---Here is a version that gets the month end dates typically used for accounting purposes

DECLARE @StartDate datetime;
 DECLARE @EndDate datetime; 
 SET @StartDate = '2010-1-1'; 
 SET @EndDate = '2020-12-31';  
 --Procedure here:   





 WITH RecursiveRowGenerator (Row#, Iteration)                             
 AS (        SELECT 1, 1         
 UNION ALL        
 SELECT Row# + Iteration, Iteration * 2         
  FROM RecursiveRowGenerator         
  WHERE Iteration * 2 < CEILING(SQRT(DATEDIFF(MONTH, @StartDate, @EndDate)+1)) 
  UNION ALL        SELECT Row# + (Iteration * 2), Iteration * 2 
           FROM RecursiveRowGenerator         
           WHERE Iteration * 2 < CEILING(SQRT(DATEDIFF(MONTH, @StartDate, @EndDate)+1))      )  
               , SqrtNRows AS (        SELECT *          FROM RecursiveRowGenerator         
 UNION ALL        SELECT 0, 0      ) 
 SELECT TOP(DATEDIFF(MONTH, @StartDate, @EndDate)+1)         
           DateAdd(d,-1,DateAdd(m,1, DATEADD(month, DATEDIFF(month, 0, @StartDate) + A.Row# * POWER(2,CEILING(LOG(SQRT(DATEDIFF(MONTH, @StartDate, @EndDate)+1))/LOG(2))) + B.Row#, 0)  ))
Row#   FROM SqrtNRows A, SqrtNRows B  ORDER BY A.Row#, B.Row#; 

Upvotes: 0

bobs
bobs

Reputation: 22184

you can do the following

SELECT DISTINCT YEAR(myDate) as [Year], MONTH(myDate) as [Month]
FROM myTable
WHERE <<appropriate criteria>>
ORDER BY [Year], [Month]

Upvotes: 0

Related Questions