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

前端 未结 8 567
悲哀的现实
悲哀的现实 2020-12-18 05:06

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

相关标签:
8条回答
  • 2020-12-18 05:48

    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
    ;
    
    0 讨论(0)
  • 2020-12-18 05:52
    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
    
    0 讨论(0)
  • 2020-12-18 05:54

    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
    
    0 讨论(0)
  • 2020-12-18 05:57

    ---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#; 
    
    0 讨论(0)
  • 2020-12-18 06:00
    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
    
    0 讨论(0)
  • 2020-12-18 06:00

    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.

    0 讨论(0)
提交回复
热议问题