计算两个指定日期之间每个月的天数

0 sql-server

我正在尝试编写一个 SQL Server 公式,该公式给出两个指定日期之间每个月的天数。例如:

在此处输入图片说明

Aar*_*and 6

这看起来像很多代码,但我认为它逐步完成并解释了它如何满足计算范围内天数的要求,包括省略下一年的天数(以防有人忘记先过滤它,示例用法也处理):

CREATE FUNCTION dbo.ReturnDateRangeByMonth
(
    @start date, @end date
)
RETURNS TABLE WITH SCHEMABINDING 
AS
RETURN 
( 
  WITH d(d) AS -- recursive to get all the days in the range
  (
    SELECT @start UNION ALL SELECT DATEADD(DAY, 1, d) FROM d
    -- except the last day 
    WHERE d < DATEADD(DAY,-1,@end) 
    -- and except any days from the next year
      AND d < DATEFROMPARTS(YEAR(@start)+1, 1, 1)
      -- on < 2012 use AND YEAR(d) = YEAR(@start)
  ),
  m(m) AS -- grab all 12 months to pivot on
  (
    SELECT 1 UNION ALL SELECT m + 1 FROM m WHERE m < 12
  )
  SELECT m.m, c = COALESCE(COUNT(d.d),0) FROM m 
  LEFT OUTER JOIN d ON m.m = MONTH(d.d) GROUP BY m.m
);
Run Code Online (Sandbox Code Playgroud)

示例用法:

DECLARE @vaca TABLE (OOW date, RTW date);

INSERT @vaca(OOW,RTW) VALUES('20191222','20200107'),('20190326','20190528'),
('20190328','20190522'),('20190612','20190617'),('20190404','20190418');

;WITH v AS 
(
  SELECT OOW, RTW FROM @vaca WHERE OOW >= '20190101' AND OOW < '20200101'
)
SELECT * FROM v CROSS APPLY dbo.ReturnDateRangeByMonth(v.OOW, v.RTW) AS f
  PIVOT (MAX(c) FOR m IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])) AS p
  OPTION (MAXRECURSION 366);
Run Code Online (Sandbox Code Playgroud)

结果:

在此处输入图片说明


Len*_*art 5

可能最容易生成开始和停止之间的日期:

with t (d) as ( 
    select cast('2017-02-10' as date) 
    union all 
    select dateadd(day, 1, d) from t where d < '2017-03-15' 
) 
select month(d), count(1) from t group by month(d);
Run Code Online (Sandbox Code Playgroud)

我认为在应用程序层进行旋转更好,但如果您坚持,您可以这样做:

with t (d) as ( 
    select cast('2017-02-10' as date) 
    union all 
    select dateadd(day, 1, d) from t where d < '2017-03-15' 
) 
select count(case when month(d) = 1 then 1 end)
     , count(case when month(d) = 2 then 1 end) 
     , count(case when month(d) = 3 then 1 end) 
 ...
from t;
Run Code Online (Sandbox Code Playgroud)

编辑:在编辑后的帖子中似乎有几个间隔。您可以考虑为日期范围创建一个函数。例子:

CREATE FUNCTION date_range(@start date, @stop date)
RETURNS TABLE AS
  RETURN
  with t (d) as ( 
    select cast(@start as date) 
    union all 
    select dateadd(day, 1, d) from t where d < @stop 
  ) 
  select d from t;

with t (x,y) as ( 
  select '2017-02-10', '2017-03-15'
  union all
  select '2017-05-01', '2017-06-21'
)
select month(dr.d), count(1) 
from t 
cross apply date_range(t.x, t.y) dr
group by month(dr.d); 
Run Code Online (Sandbox Code Playgroud)

如果要在每个间隔内计数:

with t (x,y) as ( 
  select '2017-02-10', '2017-03-15'
  union all
  select '2017-05-01', '2017-06-21'
)
select t.x, t.y, month(dr.d) as month, count(1) as cnt
from t 
cross apply date_range(t.x, t.y) dr
group by t.x, t.y, month(dr.d); 
Run Code Online (Sandbox Code Playgroud)