TSQL:递归日期按季度划分

时间:2019-05-16 09:35:08

标签: sql sql-server tsql

假设我有一个简单的查询

select
from_date, to_date
from datatable
order by from_date

这将导致

from_date   to_date
2018-01-01  2018-04-30
2018-05-01  2018-12-31

我想将日期范围分成几个季度。到目前为止,我得到的结果是:

WITH temp AS 
(
   SELECT 
   cast(sd.from_date as date) from_date, 
   cast(dateadd(day,-1,dateadd(quarter, 1, cast(sd.from_date as date))) as date)  AS end_date,
   cast(sd.to_date as date) as actual_enddate
   FROM (select cast(from_date as date) from_date, to_date from datatable) sd
   UNION ALL
   SELECT  
   dateadd(day,1,cast(t.end_date as date)) end_date, 
   CASE 
      WHEN dateadd(quarter, 1,t.end_date) < t.actual_enddate THEN dateadd(quarter, 1,t.end_date) 
      ELSE t.actual_enddate 
     END AS end_date,
   cast(t.actual_enddate as date) actual_enddate
   FROM temp t
  WHERE t.end_date != t.actual_enddate
)
SELECT 
t.from_date, 
t.end_date
FROM temp t
ORDER BY t.from_date
OPTION (MAXRECURSION 0)

我的当前输出是:

from_date   end_date
2018-01-01  2018-03-31
2018-04-01  2018-04-30
2018-05-01  2018-07-31
2018-08-01  2018-10-31
2018-11-01  2018-12-31

我想将这些记录分成完整的四分之三,以得到如下内容:

from_date   end_date
2018-01-01  2018-03-31
2018-04-01  2018-04-30
2018-05-01  2018-06-31
2018-07-01  2018-09-31
2018-10-01  2018-12-31

2 个答案:

答案 0 :(得分:1)

这与您要寻找的东西相似吗?

DECLARE @datatable TABLE
(
	from_date DATE,
	to_date DATE,
	value FLOAT
)

INSERT INTO @datatable
(from_date, to_date, value)
VALUES   
('2018-01-01','2018-04-30','100'),
('2018-05-01','2018-12-31','100')

;WITH temp AS 
(
SELECT 
	sd.from_date,
	DATEADD(DAY,-1, DATEADD(MONTH, 3 - ((MONTH(sd.from_date) -1) % 3), sd.from_date)) AS end_date,
	sd.to_date As actual_enddate, 
	sd.value
FROM @datatable sd
UNION ALL
SELECT  
	DATEADD(DAY,1, t.end_date) end_date, 
	CASE 
		WHEN DATEADD(QUARTER, 1,t.end_date) < t.actual_enddate THEN EOMONTH(DATEADD(QUARTER, 1, t.end_date) )
		ELSE t.actual_enddate 
	END AS end_date,
	t.actual_enddate As actual_enddate,
	value
FROM temp t
WHERE t.end_date != t.actual_enddate
)
SELECT 
t.from_date, 
t.end_date, 
ROUND((100.0/3.0) * (DATEDIFF(MONTH, t.from_date, DATEADD(DAY, 1, t.end_date))), 2) As Value
FROM temp t
ORDER BY t.from_date
OPTION (MAXRECURSION 0)

答案 1 :(得分:1)

此问题的日期算法相当混乱。您似乎在数月,当您使用季度结束日期时,我发现这很困难。

因此,以下内容添加了一个日期,以将值带入下个月。这仅用于计算,但简化了分区逻辑。

WITH cte AS (
      SELECT id, sd.from_date as within_quarter_start_date,
             (CASE WHEN quarter_end < to_date THEN quarter_end ELSE to_date END) as within_quarter_end_date,
             DATEADD(quarter, DATEDIFF(QUARTER, 0, from_date), 0) as quarter_start,
             v.quarter_end,
             DATEADD(day, 1, sd.to_date) as to_date, 
             sd.value, 1 as lev
      FROM datatable sd CROSS APPLY
           (VALUES (DATEADD(QUARTER, DATEDIFF(QUARTER, 0, from_date) + 1, 0))) v(quarter_end)
      UNION ALL
      SELECT id, CONVERT(DATE, DATEADD(quarter, 1, quarter_start)),
             (CASE WHEN DATEADD(quarter, 1, quarter_end) < to_date THEN DATEADD(quarter, 1, quarter_end) ELSE to_date END) as within_quarter_end_date,
             DATEADD(quarter, 1, quarter_start),
             DATEADD(quarter, 1, quarter_end),
             to_date,
             value, lev + 1
      FROM cte
      WHERE quarter_end < to_date and lev < 10
     )
select within_quarter_start_date as from_date,
       dateadd(day, -1, within_quarter_end_date) as to_date,
       value * (datediff(month, within_quarter_start_date, within_quarter_end_date) ) / 3.0,
       quarter_start, quarter_end
from cte
order by 1, 2;

Here是db <>小提琴。