2013-01-22 35 views
2

我有一個包含時間段和數量的數據庫表。把它們看成一個合同期限和每天的價格:用於合併和計算時間段的SQL查詢

start  | end  | amount_per_day 
2013-01-01 | 2013-01-31 | 100 
2013-02-01 | 2013-06-30 | 200 
2013-01-01 | 2013-06-30 | 100 
2013-05-01 | 2013-05-15 | 50 
2013-05-16 | 2013-05-31 | 50 

我想提出一個查詢,將顯示總數每個時段,即:

從2013-01-01至第一份和第三份合同是活躍的,所以每天的總金額是200.從2013年2月1日到2013年4月30日,第二和第三行是激活的,所以總數是300。從2013年5月1日至2013年5月15日,第二,三,四行均處於活動狀態,總數爲350條。2013年1月16日至2013年5月31日,第二,三,所以總數再次是350.最後,從2013-06-01到2013-06-30只有第二和第三是活躍的,所以總數回到了300.

start  | end  | total_amount_per_day 
2013-01-01 | 2013-01-31 | 200 
2013-02-01 | 2013-04-30 | 300 
2013-05-01 | 2013-05-31 | 350 
2013-06-01 | 2013-06-30 | 300 

(這是沒有必要檢測間隔2013-05-01 -> 2013-05-152013-05-16 -> 2013-05-31具有相同的總額和合並它們,但是這將是很好)。

我更喜歡可移植的解決方案,但如果不可能的話,SQL Server也可以工作。

我可以對錶格結構做一些小的改動,所以如果它能使查詢變得更簡單,例如,用時間段結束日期排除時間段(因此第一個時間段將是start = 2013-01-01,end = 2013-02-01)可以自由地提出這樣的建議。

+0

如何計算'total_amount_per_day'? –

+0

@JW .:這是否更好? –

回答

1

我將從完整的查詢開始,然後將其分解並解釋它。這是特定於SQL Server的,但只需稍作調整即可適用於任何支持分析功能的DMBS。

WITH Data AS 
( SELECT Start, [End], Amount_Per_Day 
    FROM (VALUES 
       ('20130101', '20130131', 100), 
       ('20130201', '20130630', 200), 
       ('20130101', '20130630', 100), 
       ('20130501', '20130515', 50), 
       ('20130516', '20130531', 50) 
      ) t (Start, [End], Amount_Per_Day) 
), Numbers AS 
( SELECT Number 
    FROM Master..spt_values 
    WHERE Type = 'P' 
), DailyData AS 
( SELECT [Date] = DATEADD(DAY, Number, Start), 
      [AmountPerDay] = SUM(Amount_Per_Day) 
    FROM Data 
      INNER JOIN Numbers 
       ON Number BETWEEN 0 AND DATEDIFF(DAY, Start, [End]) 
    GROUP BY DATEADD(DAY, Number, Start) 
), GroupedData AS 
( SELECT [Date], 
      AmountPerDay, 
      [GroupByValue] = DATEADD(DAY, -ROW_NUMBER() OVER(PARTITION BY AmountPerDay ORDER BY [Date]), [Date]) 
    FROM DailyData 
) 
SELECT [Start] = MIN([Date]), 
     [End] = MAX([Date]), 
     AmountPerDay 
FROM GroupedData 
GROUP BY AmountPerDay, GroupByValue 
ORDER BY [Start], [End]; 

該CTE只是您的示例數據。

Numbers CTE僅僅是一個數字0的序列 - 2047(如果你的開始和結束日期均超過2047相隔幾天,這將失敗,需要稍微適應)

下一個CTE DailyData只需使用數字擴大你的範圍到他們的個別日期,所以

20130101, 20130131, 100 

變爲

20130101, 100 
20130102, 100 
20130103, 100 
.... 
20130131, 100 

然後,它是隻是在ROW_NUMBER函數的幫助下將數據按每天的數量進行分組,以查找它何時發生變化並定義每天類似數量的範圍,然後獲取每個範圍的MIN和MAX日期。

我總是很難解釋/演示這種分組範圍的方法,如果它沒有意義,它可能是最容易看到的,如果你只是在最後使用SELECT * FROM DailyData來查看原始未集合數據

+0

謝謝!這既是一個快速反應,也是一個非常好的答案。 –