2013-03-04 76 views
2

給定以下示例查詢,在同時給定一組要排除的範圍的情況下計算日期範圍內的總天數的聲音和性能方法是什麼?範圍可能有重疊的日期?在日期範圍內計算可能重疊的排除日期

更簡單地說,我有一個表格,其中有一組日期範圍,其中關閉了帳單,我從一個日期範圍(比如Jan1 - Jan31)開始,我需要確定在該範圍內發生了多少付費天數。簡單地說,日期減去殘疾日期的日期和。然而,殘疾日期範圍有可能重疊,即在一個記錄中禁用Jan5-Jan8,在另一個記錄中禁用Jan7-Jan10-因此,簡單的總和會使Jan7增加一倍。排除這些重疊並獲得獎勵計數的最佳方法是什麼?

Declare @disableranges table (disableFrom datetime, disableTo datetime) 
insert into @disableranges 
select '01/05/2013', '01/08/2013' union 
select '01/07/2013', '01/10/2013' union 
select '01/15/2013', '01/20/2013' 

declare @fromDate datetime = '01/01/2013' 
declare @toDate datetime = '01/31/2013' 

declare @totalDays int = DATEDIFF(day,@fromDate,@toDate) 
declare @disabledDays int = (0 /*not sure best way to calc this*/) 

select @totalDays - @disabledDays 
+0

是否禁用範圍包括?即'01/05/2013'到'01/08/2013'計爲'3'天還是'4'?默認情況下,只需調用'DATEDIFF'即可包含開始日期,但由於默認時間值爲'0:00:00'(午夜),因此不包括結束日期。 – mellamokb 2013-03-04 21:23:40

+0

在我看來,它會算作3 – keithwarren7 2013-03-04 21:26:01

+0

嗯,只有你知道它應該是什麼:)。問題的關鍵在於,如果您將「01/05/2013」​​的禁用範圍設置爲「01/08/2013」​​,是否意味着「8th」也包含在內? – mellamokb 2013-03-04 21:29:08

回答

2

可以使用recursive CTE產生@dateFrom@dateTo之間的日期。然後將日期與範圍進行比較,並查找任何範圍內的所有日期。最後,計算結果中的行數以獲得禁用日期的計數(DEMO):

-- recursive CTE to generate dates 
;with dates as (
    select @fromDate as date 
    union all 
    select dateadd(day, 1, date) 
    from dates 
    where date < @toDate 
) 

-- join with disable ranges to find dates in any range 
, disabledDates as (
    select date from dates D 
    left join @disableranges R 
    on D.date >= R.disableFrom and d.Date < R.disableTo 
    group by date 
    having count(R.disablefrom) >= 1 
) 

-- count up the total disabled dates 
select @disabledDays=count(*) from disabledDates; 
2

就我而言,試過這個和工作正常。

Declare @disableranges table (disableFrom datetime, disableTo datetime) 
insert into @disableranges 
select '01/05/2013', '01/08/2013' union 
select '01/07/2013', '01/10/2013' union 
select '01/15/2013', '01/20/2013' 

declare @fromDate datetime = '01/01/2013' 
declare @toDate datetime = '01/31/2013' 

declare @totalDays int = DATEDIFF(day,@fromDate,@toDate) + 1 /*Without +1 it is giving 30 instead of 31*/ 
declare @disabledDays int = (0 /*not sure best way to calc this*/) 
/*Fill temporary table with the given date range.*/ 
SELECT DATEADD(DAY, nbr - 1, @fromDate) TempDate INTO #Temp 
FROM (SELECT ROW_NUMBER() OVER (ORDER BY c.object_id) AS Nbr 
      FROM  sys.columns c 
     ) nbrs 
WHERE nbr - 1 <= DATEDIFF(DAY, @fromDate, @toDate) 
/*Check how many dates exists in the disableranges table*/ 
SELECT @disabledDays=count(*) from #Temp t WHERE 
EXISTS(SELECT * FROM @disableranges 
WHERE t.TempDate BETWEEN disableFrom AND DATEADD(d, -1, disableTo)) 

select @totalDays /*Output:31*/ 
select @disabledDays /*Output:10*/ 
select @totalDays - @disabledDays /*Output:21*/ 
drop table #Temp 

採取幫助從答案https://stackoverflow.com/a/7825036/341117填寫表的日期範圍