2008-10-13 40 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 

,你應該能夠創建靜態表,你會需要高效地執行多個日期範圍查詢。