t-sql select在几年内获得所有月份

时间:2010-11-15 03:57:18

标签: sql sql-server tsql sql-server-2008

我需要一个选择来返回月份和年份在指定的日期范围内我将输入开始年份和月份,而选择将从我输入的日期到今天返回月份和年份。

我知道我可以循环执行此操作,但我想知道是否可以在一系列选择中执行此操作?

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

等等。

8 个答案:

答案 0 :(得分:15)

天哪...使用“计数递归CTE”或“rCTE”与使用循环一样糟或更差。请参阅以下文章,了解我的原因。

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

这是一种没有任何RBAR的方法,包括计数rCTE的“隐藏RBAR”。

--===== 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
;

答案 1 :(得分:3)

您可以使用以下内容:http://blogs.msdn.com/b/sqlazure/archive/2010/09/16/10063301.aspx

使用日期范围生成数字表的等价物。

但是,请您澄清一下您的输入和输出吗?

您是否要输入开始日期,例如'2010-5-1'和结束日期,例如'2010-8-1'并让它在两者之间每月返回一次?您想要包括开始月份和结束月份,还是要将其排除?

以下是我编写的一些代码,可以快速生成两个日期之间每月的包容性结果。

--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#;

答案 2 :(得分:2)

以下代码生成 2013年7月21日 2014年1月15日之间的范围值。 我通常在SSRS报告中使用它来为Month参数生成查找值。

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

结果:

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

答案 3 :(得分:1)

---这是一个获取通常用于会计目的的月末日期的版本

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#; 

答案 4 :(得分:1)

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

答案 5 :(得分:0)

您可以执行以下操作

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

我知道这是一个老问题,但是我对某些答案的复杂性感到有些恐惧。使用CTE绝对是选择这些值的最简单方法:

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);

只需在您想要的开始日期前代替上面的'2017-07-01',就可以使用高效且易于集成的解决方案。


编辑: Jeff Moden's answer非常有效地主张不要使用rCTE。但是,在这种情况下,这似乎是过早优化的情况-我们极有可能谈论10条记录,即使您从今天开始回溯到1900年,它的影响也很小。如果预期结果集很小,那么使用rCTE来实现代码可维护性似乎是值得的。