2013-11-28 37 views
5

我有兩個列的表名爲「FROM_DATE」和「TO_DATE」裂棗範圍到SQL服務器每月一行

the table look like:- 

enter image description here

我想造成這樣的: -

from_date   to_date 
-----------   ------------ 
2013-11-25   2013-11-30 
2013-12-01   2013-12-05 

該日期從2013-11-25分到2013-11-30,另一個日期從2013-12-01分到2013-12-05 ...是否可以這樣分割?

+1

以下是*幾乎*相同。它只適用於整整數月,但它應該是微不足道的修改它在部分月份以及以下工作:http://stackoverflow.com/questions/7218526/split-date-range-into-months – Heinzi

+0

如果日期跨度3個月例如「2013-11-25」到「2014-02-05」應該分成3行還是4行? – GarethD

回答

5

這是閏年安全,處理日期範圍,其他答案目前沒有。

DECLARE @d TABLE(from_date DATE, to_date DATE); 

INSERT @d VALUES ('2013-11-25','2013-12-05'); 

;WITH n(n) AS 
(
    SELECT ROW_NUMBER() OVER (ORDER BY [object_id])-1 FROM sys.all_columns 
), 
d(n,f,t,md,bp,ep) AS 
(
    SELECT n.n, d.from_date, d.to_date, 
    DATEDIFF(MONTH, d.from_date, d.to_date), 
    DATEADD(MONTH, n.n, DATEADD(DAY, 1-DAY(from_date), from_date)), 
    DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(MONTH, n.n, 
     DATEADD(DAY, 1-DAY(from_date), from_date)))) 
FROM n INNER JOIN @d AS d 
ON d.to_date >= DATEADD(MONTH, n.n-1, d.from_date) 
) 
SELECT original_from_date = f, original_to_date = t, 
    new_from_date = CASE n WHEN 0 THEN f ELSE bp END, 
    new_to_date = CASE n WHEN md THEN t ELSE ep END 
FROM d WHERE md >= n 
ORDER BY original_from_date, new_from_date; 

結果:

original_from_date original_to_date new_from_date new_to_date 
------------------ ---------------- ------------- ----------- 
2013-11-25   2013-12-05   2013-11-25  2013-11-30 
2013-11-25   2013-12-05   2013-12-01  2013-12-05 

SQLFiddle demo with longer date ranges and leap years

+0

這就是我想要的......你的查詢工作正常@Aaron Bertrand。 –

+0

如果第一個from_date的月份數少於31天,那麼您的解決方案無法正確查找該月的最後一天。從十月開始,而不是十一月。 10月份在9月份之後,9月份不到31天。 – Kennah

+0

我沒有找到這個。 Martijn Vermunt做到了。 http://stackoverflow.com/q/34514361/2471473 – Kennah

1

如果你是在一個三維的數據倉庫操作,使用日期維度。否則,使用CTE。

WITH cte AS 
(SELECT from_date 
     , to_date 
     , from_date AS mo_from_date 
     , DATEADD(day, day(from_date)* -1 + 1, from_date) AS bom_date 
    FROM DateTable 
UNION ALL 
SELECT from_date 
    , to_date 
    , DATEADD(month,1,bom_date) 
    , DATEADD(month,1,bom_date) 
    FROM cte 
where DATEADD(month,1,mo_from_date) < to_date 
) 
SELECT mo_from_date 
    , CASE when to_date < DATEADD(month,1,bom_date) THEN 
      to_date 
     ELSE 
      DATEADD(day, -1, DATEADD(month,1,bom_date)) 
     END AS mo_to_date 
    FROM cte