2015-10-19 106 views
7

我需要做的是計算給定一個表歷年之內丟失的時間段,如在SQL中:SQL計算日期段

DatesTable 
|ID|DateStart |DateEnd | 
1 NULL   NULL 
2 2015-1-1  2015-12-31 
3 2015-3-1  2015-12-31 
4 2015-1-1  2015-9-30 
5 2015-1-1  2015-3-31 
5 2015-6-1  2015-12-31 
6 2015-3-1  2015-6-30 
6 2015-7-1  2015-10-31 

預期回報率將是:

1 2015-1-1  2015-12-31 
3 2015-1-1  2015-2-28 
4 2015-10-1 2015-12-31 
5 2015-4-1  2015-5-31 
6 2015-1-1  2015-2-28 
6 2015-11-1 2015-12-31 

它本質上是工作塊。我需要展示的是日曆年中沒有工作的部分。所以對於ID = 3,他從3/1開始工作到今年的其餘時間。但他從1/1到2/28沒有工作。這就是我要找的。

+0

你能解釋一下結果的邏輯嗎,比如記錄ID = 3嗎? –

+2

這是SQL Server 2008中的一個痛點。你可以升級到SQL Server 2012嗎? –

+0

考慮完成。如果有幫助,我可以在2012年運行。 – Brian

回答

0

如果你的數據不是太大,這種方法將起作用。它擴展了所有的日子和ID,然後重新它們分組:

with d as (
     select cast('2015-01-01' as date) 
     union all 
     select dateadd(day, 1, d) 
     from d 
     where d < cast('2015-12-31' as date) 
    ), 
    td as (
     select * 
     from d cross join 
      (select distinct id from t) t 
     where not exists (select 1 
         from t t2 
         where d.d between t2.startdate and t2.enddate 
         ) 
    ) 
select id, min(d) as startdate, max(d) as enddate 
from (select td.*, 
      dateadd(day, - row_number() over (partition by id order by d), d) as grp 
     from td 
    ) td 
group by id, grp 
order by id, grp; 

的另一種方法依賴於累積和以及類似功能,是SQL Server 2012+來表達容易得多。

+0

我正在運行SQL Server 2012.我已更改與問題關聯的標記。 – Brian

+0

這將在SQL Server 2012中工作。 –

1

可以使用LEAD做到這一點,LAG可以從SQL Server 2012+窗口功能:

;WITH CTE AS (
    SELECT ID, 
      LAG(DateEnd) OVER (PARTITION BY ID ORDER BY DateEnd) AS PrevEnd, 
      DateStart, 
      DateEnd, 
      LEAD(DateStart) OVER (PARTITION BY ID ORDER BY DateEnd) AS NextStart 
    FROM DatesTable 
) 
SELECT ID, DateStart, DateEnd 
FROM (
-- Get interval right before current [DateStart, DateEnd] interval 
SELECT ID, 
     CASE 
      WHEN DateStart IS NULL THEN '20150101' 
      WHEN DateStart > start THEN start 
      ELSE NULL 
     END AS DateStart, 
     CASE 
      WHEN DateStart IS NULL THEN '20151231' 
      WHEN DateStart > start THEN DATEADD(d, -1, DateStart) 
      ELSE NULL 
     END AS DateEnd 
FROM CTE 
CROSS APPLY (SELECT COALESCE(DATEADD(d, 1, PrevEnd), '20150101')) x(start) 

-- If there is no next interval then get interval right after current 
-- [DateStart, DateEnd] interval (up-to end of year) 
UNION ALL 

SELECT ID, DATEADD(d, 1, DateEnd) AS DateStart, '20151231' AS DateEnd  
FROM CTE 
WHERE DateStart IS NOT NULl -- Do not re-examine [Null, Null] interval 
     AND NextStart IS NULL -- There is no next [DateStart, DateEnd] interval 
     AND DateEnd < '20151231' -- Current [DateStart, DateEnd] interval 
           -- does not terminate on 31/12/2015 
) AS t 
WHERE t.DateStart IS NOT NULL 
ORDER BY ID, DateStart 

上面的查詢背後的想法很簡單:每[DateStart, DateEnd]間隔獲得「沒有工作」區間權在它之前。如果在當前時間間隔之後沒有間隔,那麼也會得到連續的'未工作'間隔(如果有的話)。

另外請注意,我認爲如果DateStartNULL然後DateStartNULL爲同一ID

Demo here

0

有些簡單的方法,我想。

基本上爲所有工作塊範圍創建一個日期列表(A)。然後爲每個ID(B)創建一整年的日期列表。然後從B中刪除A.將剩餘日期列表編譯爲每個ID的日期範圍。

DECLARE @startdate DATETIME, @enddate DATETIME 
SET @startdate = '2015-01-01' 
SET @enddate = '2015-12-31' 

--Build date ranges from remaining date list 
;WITH dateRange(ID, dates, Grouping) 
AS 
(
    SELECT dt1.id, dt1.Dates, dt1.Dates + row_number() over (order by dt1.id asc, dt1.Dates desc) AS Grouping 
    FROM 
    (
     --Remove (A) from (B) 
     SELECT distinct dt.ID, tmp.Dates FROM DatesTable dt 
     CROSS APPLY 
     (
      --GET (B) here 
      SELECT DATEADD(DAY, number, @startdate) [Dates] 
      FROM master..spt_values 
      WHERE type = 'P' AND DATEADD(DAY, number, @startdate) <= @enddate 
     ) tmp 
     left join 
     (
      --GET (A) here 
      SELECT DISTINCT T.Id, 
       D.Dates 
      FROM DatesTable AS T 
      INNER JOIN master..spt_values as N on N.number between 0 and datediff(day, T.DateStart, T.DateEnd) 
      CROSS APPLY (select dateadd(day, N.number, T.DateStart)) as D(Dates) 
      WHERE N.type ='P' 
     ) dr 
     ON dr.Id = dt.Id and dr.Dates = tmp.Dates 
     WHERE dr.id is null 
    ) dt1 
) 
SELECT ID, CAST(MIN(Dates) AS DATE) DateStart, CAST(MAX(Dates) AS DATE) DateEnd 
FROM dateRange 
GROUP BY ID, Grouping 
ORDER BY ID 

繼承人的代碼: http://sqlfiddle.com/#!3/f3615/1

我希望這有助於!