TSQL: рекурсивная дата, разделенная на квартал - PullRequest
1 голос
/ 16 мая 2019

Допустим, у меня есть простой запрос

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 ]

1 голос
/ 16 мая 2019

Арифметика даты для этой проблемы довольно запутанная.Похоже, вы подсчитываете месяцы, и мне трудно, когда вы используете дату окончания квартала.

Поэтому ниже добавлена ​​одна дата для переноса значений в следующий месяц.Это только для вычисления, но оно упрощает логику разделения.

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;

Здесь - это db <> скрипка.

1 голос
/ 16 мая 2019

Это похоже на то, что вы ищете?

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...