我需要一个选择来返回月份和年份在指定的日期范围内我将输入开始年份和月份,而选择将从我输入的日期到今天返回月份和年份。
我知道我可以循环执行此操作,但我想知道是否可以在一系列选择中执行此操作?
Year Month
---- -----
2010 1
2010 2
2010 3
2010 4
2010 5
2010 6
2010 7
等等。
答案 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来实现代码可维护性似乎是值得的。