【问题标题】:Split date range into one row per month in sql server在sql server中每月将日期范围拆分为一行
【发布时间】:2013-11-28 15:34:38
【问题描述】:

我有一个包含两列的表,分别称为“from_date”和“to_date”

    the table look like:-

我想要这样的结果:-

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... 可以这样拆分吗?

【问题讨论】:

  • 以下是几乎相同的。它仅适用于整月,但将其修改为也适用于部分月应该是微不足道的:stackoverflow.com/questions/7218526/…
  • 如果日期跨越 3 个月,例如2013-11-252014-02-05 应该分成 3 行还是 4 行?

标签: sql sql-server


【解决方案1】:

这是闰年安全的,并且可以处理其他答案目前没有的日期范围。

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

【讨论】:

  • 如果第一个 from_date 是下一个月份少于 31 天的月份,则您的解决方案无法正确找到该月的最后一天。从 10 月开始,而不是 11 月。 10 月紧随 9 月,而 9 月不到 31 天。
  • 我没有找到这个。 Martijn Vermunt 做到了。 stackoverflow.com/q/34514361/2471473
  • @AaronBertrand,你先生,ROCK!
  • 如果我有时间陪我的约会对象怎么办?当我用日期定义时间时,我尝试了上面的查询,但分成几个月工作错误
【解决方案2】:

如果您在维度数据仓库中操作,请使用日期维度。否则,使用 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

【讨论】:

    猜你喜欢
    • 2016-04-03
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    相关资源
    最近更新 更多