【问题标题】:Date ranges in T/SQLT/SQL 中的日期范围
【发布时间】:2008-10-13 00:42:18
【问题描述】:

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

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

我将如何实现这个功能?

【问题讨论】:

    标签: sql-server


    【解决方案1】:

    静态数字表很有用,单列,比如 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
    

    【讨论】:

      【解决方案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
      

      【讨论】:

        【解决方案3】:

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

        Generate Ranges In SQL

        【讨论】:

        • 这可以工作,但是我发现它没有 mssql 的表函数解决方案那么优雅。如果我真的需要在许多不同的数据库中工作的东西,那么基于集合的选项可能是唯一的选择。
        【解决方案4】:

        从性能的角度来看,您不会希望使用函数来生成日期范围。对于查询中的每个评估 (@myDate &gt; 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 开始的每一天的零小时 (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 的不同组合,您应该能够创建高效地执行许多日期范围查询所需的静态表。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-07
          • 1970-01-01
          相关资源
          最近更新 更多