2008-10-13 38 views
2

对于我正在使用的当前项目,我需要根据日期范围返回汇总报告。T/SQL中的日期范围

我有3种类型的报告,每年,每月和每日。

为了帮助返回这份报告,我需要一个函数来返回大范围内的所有日期时间的子范围。

因此,例如,如果我作为'2006-01-01 11:10:00'和'2006-01-05 08:00:00'之间的所有日常范围,我会期待以下结果。

select * 
from dbo.fnGetDateRanges('d', '2006-01-01 11:10:00', '2006-01-05 08:00:00') 

2006-01-01 11:10:00.000, 2006-01-02 00:00:00.000 
2006-01-02 00:00:00.000, 2006-01-03 00:00:00.000 
2006-01-03 00:00:00.000, 2006-01-04 00:00:00.000 
2006-01-04 00:00:00.000, 2006-01-05 00:00:00.000 
2006-01-05 00:00:00.000, 2006-01-05 08:00:00.000 

对于'2006-01-01 11:10:00'到'2009-05-05 08:00:00'的年度范围,我会期待。

select * 
from dbo.fnGetDateRanges('y', '2006-01-01 11:10:00', '2009-05-05 08:00:00') 

2006-01-01 11:10:00.000, 2007-01-01 00:00:00.000 
2007-01-01 00:00:00.000, 2008-01-01 00:00:00.000 
2008-01-01 00:00:00.000, 2009-01-01 00:00:00.000 
2009-01-01 00:00:00.000, 2009-05-05 08:00:00.000 

我该如何实现这个功能?

回答

2

有在这里相当一些技巧,希望你觉得它有用

create function dbo.fnGetDateRanges 
(
    @type char(1), 
    @start datetime, 
    @finish datetime 
) 
returns @ranges table(start datetime, finish datetime) 
as 
begin 

    declare @from datetime 
    declare @to datetime 
    set @from = @start 

    if @type = 'd' 
    begin 
     set @to = dateadd(day, 1, 
       convert 
       ( datetime, 
        cast(DatePart(d,@start) as varchar) + '/' + cast(DatePart(m,@start) as varchar) + '/' + cast(DatePart(yy,@start) as varchar), 
        103 
       ) 
      ) 
    end 

    if @type = 'm' 
    begin 
     set @to = dateadd(month, 1, 
      convert 
      ( 
       datetime, 
       '1/' + cast(DatePart(m,@start) as varchar) + '/' + cast(DatePart(yy,@start) as varchar), 
       103 
      ) 
     ) 
    end 

    if @type = 'y' 
    begin 
     set @to = dateadd(year, 1, 
      convert 
      ( 
       datetime, 
       '1/1/' + cast(DatePart(yy,@start) as varchar), 
       103 
      ) 
     ) 
    end 

    while @to < @finish 
    begin 
     insert @ranges values (@from, @to) 
     set @from = @to 
     if @type = 'd' 
      set @to = dateadd(day, 1, @to) 
     if @type = 'm' 
      set @to = dateadd(month, 1, @to) 
     if @type = 'y' 
      set @to = dateadd(year, 1, @to) 
    end 

    insert @ranges values (@from, @finish) 

    return 
end 
1

如果你喜欢一个基于集合的解决方案,使用像下面的链接所示的一个战术产生一系列的数字值从x到y。然后,加入DATEADD()和您自己的自定义限制来创建天,月,季,年等范围。我发现将这个范围查询作为视图是很有帮助的。

Generate Ranges In SQL

+0

这可以工作,但是我发现它比mssql的表函数解决方案少一点优雅。如果我真的需要一些适用于很多不同dbs的东西,那么基于集合的选项可能是唯一的选择。 – 2008-10-13 06:12:59

3

静态数字表是有用的,单柱,说8000行从0到7999

(未选中)

DECLARE @Start smalldatetime, @End smalldatetime, @Diff int 

SELECT @Start = '2006-01-01 11:10:00', @End = '2009-05-05 08:00:00', @diff = DATEDIFF(year,@start,@end) 

SELECT 
    DATEADD(year,N.Number,@Start) 
FROM 
    dbo.Number N 
WHERE 
    N.Number <= @diff 
1

从性能的角度来看,你不会想使用函数来生成日期范围。对于查询中的每个评估(@myDate > dbo.MyFunc()),该功能必须完全执行。你最好的选择是建立静态数字表。

现在在用数字表....

这是创建一个整数表的快捷方式。 (用于身份欺骗的道具到Jeff Moden)

SELECT TOP 1000000 
     IDENTITY(INT,1,1) as N 
    INTO dbo.NumbersTable 
    FROM Master.dbo.SysColumns 
     Master.dbo.SysColumns 

在表中填充1000000个数字的时间少于2秒。

现在要解决您的问题,您将需要使用它来建立一个日期表。下面的例子将创建零时(12AM)每一天的表从@startDate与DATEADD/DIFF的不同组合,从现在开始,

DECLARE @DaysFromStart int 
DECLARE @StartDate datetime 
SET @StartDate = '10/01/2008' 

SET @ DaysFromStart = (SELECT (DATEDIFF(dd,@StartDate,GETDATE()) + 1)) 

CREATE TABLE [dbo].[TableOfDates](
    [fld_date] [datetime] NOT NULL, 
CONSTRAINT [PK_TableOfDates] PRIMARY KEY CLUSTERED 
(
    [fld_date] ASC 
)WITH FILLFACTOR = 99 ON [PRIMARY] 
) ON [PRIMARY] 


INSERT INTO 
    dbo.TableOfDates 
SELECT 
     DATEADD(dd,nums.n - @DaysFromStart ,CAST(FLOOR(CAST(GETDATE() as FLOAT)) as DateTime)) as FLD_Date 
FROM #NumbersTable nums 

SELECT MIN(FLD_Date) FROM dbo.TableOfDates 
SELECT MAX(FLD_Date) FROM dbo.TableOfDates 

,你应该能够创建静态表,你会需要高效地执行多个日期范围查询。