如何确定给定日期范围内一个月中的天数?
How to determine the number of days in a month for a given Date Range?
我需要使用SQL查询来计算每个日历月在给定范围内的天数。
我给了2个日期,它们定义了一个日期范围;例如 2020-01-01 到 2020-08-03。我需要找出每个月在该范围内的天数,即七月有多少天,八月有多少天。
在给定的示例中,预期结果是 7 月 31 天和 8 月 3 天。
你可以参考这个
;with dates(thedate) as (
select dateadd(yy,years.number,0)+days.number
from master..spt_values years
join master..spt_values days
on days.type='p' and days.number < datepart(dy,dateadd(yy,years.number+1,0)-1)
where years.type='p' and years.number between 100 and 150
-- note: 100-150 creates dates in the year range 2000-2050
-- adjust as required
)
select dateadd(m,datediff(m, 0, d.thedate),0) themonth, count(1)
from dates d
where d.thedate between '2020-01-01' and '2020-08-03'
group by datediff(m, 0, d.thedate)
order by themonth;
请参考下面的 link,RichardTheKiwi 用户为您的场景提供了一个清晰的示例。
SQL Server query for total number of days for a month between date ranges
一种方法使用递归查询。使用日期算法,我们可以构建查询,使其每月执行一次迭代而不是每天一次,因此这应该是一种相当有效的方法:
with cte as (
select
datefromparts(year(@dt_start), month(@dt_start), 1) month_start,
1 - day(@dt_start) + day(
case when @dt_end > eomonth(@dt_start)
then eomonth(@dt_start)
else @dt_end
end
) as no_days
union all
select
dateadd(month, 1, month_start),
case when @dt_end > dateadd(month, 2, month_start)
then day(eomonth(dateadd(month, 1, month_start)))
else day(@dt_end)
end
from cte
where dateadd(month, 1, month_start) <= @dt_end
)
select * from cte
如果我们设置边界如下:
declare @dt_start date = '2020-07-10';
declare @dt_end date = '2020-09-10';
然后查询returns:
month_start | no_days
:---------- | ------:
2020-07-01 | 22
2020-08-01 | 31
2020-09-01 | 10
您可以在月级别而不是天级别完成所有工作 -- 这应该会更快一些。这是一个使用递归 CTE 的方法:
with cte as (
select @startdate as startdate, @enddate as enddate,
datefromparts(year(@startdate), month(@startdate), 1) as month
union all
select startdate, enddate, dateadd(month, 1, month)
from cte
where dateadd(month, 1, month) < @enddate
)
select month,
(case when month <= startdate and dateadd(month, 1, month) >= enddate
then day(enddate) - day(startdate) + 1
when month <= startdate
then day(eomonth(month)) - day(startdate) + 1
when dateadd(month, 1, month) < enddate
then day(eomonth(month))
when dateadd(month, 1, month) >= enddate
then day(enddate)
end)
from cte;
还有 db<>fiddle.
日级别的逻辑更简单:
with cte as (
select @startdate as dte, @enddate as enddate
union all
select dateadd(day, 1, dte), enddate
from cte
where dte < enddate
)
select datefromparts(year(dte), month(dte), 1) as yyyymm, count(*)
from cte
group by datefromparts(year(dte), month(dte), 1)
order by yyyymm
option (maxrecursion 0)
这是一个递归 CTE 的解决方案。
declare @startDate date = '2020-07-01'
declare @endDate date = '2020-08-03'
; WITH cte (n, year, month, daycnt)
AS (
SELECT
0
, DATEPART(year, @startDate)
, DATENAME(MONTH, @startDate)
, DATEPART(day, EOMONTH( @startDate ) ) - DATEPART(day, @startDate ) + 1
UNION ALL
SELECT
n + 1
, DATEPART(year, DATEADD(month, n + 1, @startDate) )
, DATENAME(MONTH, DATEADD(month, n + 1, @startDate) )
, IIF(
n = ( DATEPART(month, @endDate) - DATEPART(month, @startDate) ) + ( DATEPART(year, @endDate) - DATEPART(year, @startDate) ) * 12 - 1
, DATEPART(day, @endDate )
, DATEPART(day, EOMONTH( DATEADD(month, n + 1, @startDate) ) )
)
FROM
cte
WHERE
n <= ( DATEPART(month, @endDate) - DATEPART(month, @startDate) ) + ( DATEPART(year, @endDate) - DATEPART(year, @startDate) ) * 12 - 1
)
SELECT *
FROM cte
ORDER BY n
OPTION (maxrecursion 0)
这可以用数字函数进一步简化,但它本质上也是一个递归 CTE,尽管它看起来肯定更清晰。但它需要在此 SELECT 语句之上定义一个函数。
我需要使用SQL查询来计算每个日历月在给定范围内的天数。
我给了2个日期,它们定义了一个日期范围;例如 2020-01-01 到 2020-08-03。我需要找出每个月在该范围内的天数,即七月有多少天,八月有多少天。
在给定的示例中,预期结果是 7 月 31 天和 8 月 3 天。
你可以参考这个
;with dates(thedate) as (
select dateadd(yy,years.number,0)+days.number
from master..spt_values years
join master..spt_values days
on days.type='p' and days.number < datepart(dy,dateadd(yy,years.number+1,0)-1)
where years.type='p' and years.number between 100 and 150
-- note: 100-150 creates dates in the year range 2000-2050
-- adjust as required
)
select dateadd(m,datediff(m, 0, d.thedate),0) themonth, count(1)
from dates d
where d.thedate between '2020-01-01' and '2020-08-03'
group by datediff(m, 0, d.thedate)
order by themonth;
请参考下面的 link,RichardTheKiwi 用户为您的场景提供了一个清晰的示例。
SQL Server query for total number of days for a month between date ranges
一种方法使用递归查询。使用日期算法,我们可以构建查询,使其每月执行一次迭代而不是每天一次,因此这应该是一种相当有效的方法:
with cte as (
select
datefromparts(year(@dt_start), month(@dt_start), 1) month_start,
1 - day(@dt_start) + day(
case when @dt_end > eomonth(@dt_start)
then eomonth(@dt_start)
else @dt_end
end
) as no_days
union all
select
dateadd(month, 1, month_start),
case when @dt_end > dateadd(month, 2, month_start)
then day(eomonth(dateadd(month, 1, month_start)))
else day(@dt_end)
end
from cte
where dateadd(month, 1, month_start) <= @dt_end
)
select * from cte
如果我们设置边界如下:
declare @dt_start date = '2020-07-10';
declare @dt_end date = '2020-09-10';
然后查询returns:
month_start | no_days :---------- | ------: 2020-07-01 | 22 2020-08-01 | 31 2020-09-01 | 10
您可以在月级别而不是天级别完成所有工作 -- 这应该会更快一些。这是一个使用递归 CTE 的方法:
with cte as (
select @startdate as startdate, @enddate as enddate,
datefromparts(year(@startdate), month(@startdate), 1) as month
union all
select startdate, enddate, dateadd(month, 1, month)
from cte
where dateadd(month, 1, month) < @enddate
)
select month,
(case when month <= startdate and dateadd(month, 1, month) >= enddate
then day(enddate) - day(startdate) + 1
when month <= startdate
then day(eomonth(month)) - day(startdate) + 1
when dateadd(month, 1, month) < enddate
then day(eomonth(month))
when dateadd(month, 1, month) >= enddate
then day(enddate)
end)
from cte;
还有 db<>fiddle.
日级别的逻辑更简单:
with cte as (
select @startdate as dte, @enddate as enddate
union all
select dateadd(day, 1, dte), enddate
from cte
where dte < enddate
)
select datefromparts(year(dte), month(dte), 1) as yyyymm, count(*)
from cte
group by datefromparts(year(dte), month(dte), 1)
order by yyyymm
option (maxrecursion 0)
这是一个递归 CTE 的解决方案。
declare @startDate date = '2020-07-01'
declare @endDate date = '2020-08-03'
; WITH cte (n, year, month, daycnt)
AS (
SELECT
0
, DATEPART(year, @startDate)
, DATENAME(MONTH, @startDate)
, DATEPART(day, EOMONTH( @startDate ) ) - DATEPART(day, @startDate ) + 1
UNION ALL
SELECT
n + 1
, DATEPART(year, DATEADD(month, n + 1, @startDate) )
, DATENAME(MONTH, DATEADD(month, n + 1, @startDate) )
, IIF(
n = ( DATEPART(month, @endDate) - DATEPART(month, @startDate) ) + ( DATEPART(year, @endDate) - DATEPART(year, @startDate) ) * 12 - 1
, DATEPART(day, @endDate )
, DATEPART(day, EOMONTH( DATEADD(month, n + 1, @startDate) ) )
)
FROM
cte
WHERE
n <= ( DATEPART(month, @endDate) - DATEPART(month, @startDate) ) + ( DATEPART(year, @endDate) - DATEPART(year, @startDate) ) * 12 - 1
)
SELECT *
FROM cte
ORDER BY n
OPTION (maxrecursion 0)
这可以用数字函数进一步简化,但它本质上也是一个递归 CTE,尽管它看起来肯定更清晰。但它需要在此 SELECT 语句之上定义一个函数。