T / SQL中的日期范围

时间:2008-10-13 00:42:19

标签: sql-server

对于我正在工作的当前项目,我需要根据日期范围返回汇总报告。

我有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

我该如何实现这个功能?

4 个答案:

答案 0 :(得分:3)

静态数字表很有用,单列,比如8000行FROM 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 :(得分: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

答案 2 :(得分:1)

如果您更喜欢基于集合的解决方案,请使用以下链接中显示的策略来生成从x到y的一系列数值。然后,使用DATEADD()和您自己的自定义限制加入它,以创建天,月,季,年或其他任何范围。我发现将此范围查询作为视图很有帮助。

Generate Ranges In SQL

答案 3 :(得分: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秒。

现在要解决您的问题,您需要使用它来构建日期表。以下示例将从@startDate

开始创建一个每小时0小时(12AM)的表格
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

现在使用DATEADD / DIFF的不同组合,您应该能够创建有效执行许多日期范围查询所需的静态表。