简体   繁体   English

SQL Server查询日期范围之间一个月的总天数

[英]SQL Server query for total number of days for a month between date ranges

I have specific date range like 我有特定的日期范围,例如

From Date        To Date
---------------------------    
2012-11-10       2012-11-15
2012-11-21       2012-11-22
2012-11-30       2012-12-01

I want to write a SQL query which calculates the total no of days between two dates and sum total number of days of particular month 我想编写一个SQL查询,该查询计算两个日期之间的天数总数和特定月份的天数总数

The output I wanted is, 我想要的输出是

No of days     month
--------------------
   9            11
   1            12

Can anyone help me to write this SQL query? 谁能帮我写这个SQL查询吗?

Ideally, you have a table named "Dates" with all the dates you will ever use, eg year 1950 through 2100. This query will give you the result you want: 理想情况下,您有一个名为“日期”的表,其中包含您将要使用的所有日期,例如1950年至2100年。此查询将为您提供所需的结果:

  select dateadd(m,datediff(m, 0, d.thedate),0) themonth, count(1)
    from dates d
    join ranges r on d.thedate between r.[from date] and r.[to date]
group by datediff(m, 0, d.thedate)
order by themonth;

Result: 结果:

|   themonth | COLUMN_1 |
-------------------------
| 2012-11-01 |        9 |
| 2012-12-01 |        1 |

Note that instead of just showing "11" or "12" as month, which doesn't work well if you have ranges going above 12 months, or doesn't help sorting when it crosses a new year, this query shows the first day of the month instead. 请注意,此查询显示的是第一天,而不是仅将“ 11”或“ 12”显示为月份,如果您的范围超过12个月或者在新的一年中无济于事时,则不能很好地工作代替一个月。

If not, you can virtually create a dates table on the fly, per the expanded query below: 如果没有,您可以按照下面的扩展查询在运行中虚拟地创建一个dates表:

;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
    join ranges r on d.thedate between r.[from date] and r.[to date]
group by datediff(m, 0, d.thedate)
order by themonth;

The full working sample is given here: SQL Fiddle 此处提供了完整的工作示例: SQL Fiddle

尝试这个

select ((day(date_to)) - (day(date_from))) as no_of_days,month(date_from)as month from tablename 

Pardon me for badly written SQL. 请原谅我编写的SQL不好。
The assumption is that the month diff. 假设是月份差异。 between fromdate and todate is 1. 在fromdate和todate之间是1。

Schema 架构

CREATE TABLE dateData
    (fromdate datetime, todate datetime)
;

INSERT INTO dateData
    (fromdate, todate)
VALUES
    ('2012-11-10', '2012-11-15'),
    ('2012-11-21', '2012-11-22'),
    ('2012-11-30', '2012-12-01')
;

SQL SQL

select mth, sum(days) as daysInMth
from
(
select month(fromdate) as mth,
sum(case 
when month(fromdate) = month(todate) then datediff(dd, fromdate, todate)+1
else datediff(dd, fromdate, dateadd(mm, 1, fromdate) - day(fromdate)) + 1 end)
as days 
from dateData
group by month(fromdate)
union
select month(todate) as mth,
sum(case when month(todate) <> month(fromdate) then
datediff(dd, fromdate, dateadd(mm, 1, fromdate) - day(fromdate)) + 1
else 
case when month(todate) = month(fromdate) then 0 else
datediff(dd, convert(datetime, year(todate) + '-' + month(todate) + '-1'), todate) 
end
end) as days
from dateData
group by month(todate)
) aggregated
group by mth

View on SQLFiddle: http://www.sqlfiddle.com/#!3/9f7da/56 在SQLFiddle上查看: http ://www.sqlfiddle.com/#!3 / 9f7da/56

I'd break it down into several steps (each given a separate CTE): 我将其分为几个步骤(每个步骤都有一个单独的CTE):

declare @Ranges table (FromDate date not null,ToDate date not null)
insert into @Ranges (FromDate,ToDate) values
('20121110','20121115'),
('20121121','20121122'),
('20121130','20121201')

;with Months as (
    select
        DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010101') as MonthStart,
        DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010131') as MonthEnd
    from @Ranges
    union /* not all */
    select
        DATEADD(month,DATEDIFF(month,'20010101',ToDate),'20010101') as MonthStart,
        DATEADD(month,DATEDIFF(month,'20010101',ToDate),'20010131') as MonthEnd
    from @Ranges
), MonthRanges as (
    select
        CASE WHEN r.FromDate > m.MonthStart then r.FromDate ELSE m.MonthStart END as StartRange,
        CASE WHEN r.ToDate < m.MonthEnd then r.ToDate ELSE m.MonthEnd END as EndRange
    from
        @Ranges r
            inner join
        Months m
            on
                r.ToDate >= m.MonthStart and
                r.FromDate <= m.MonthEnd
)
select
    DATEPART(month,StartRange),
    SUM(DATEDIFF(day,StartRange,EndRange)+1) /* Inclusive */
from
    MonthRanges
group by
    DATEPART(month,StartRange)

First, the Months CTE finds the first and last days of each month that we might be interested in(*). 首先, Months CTE查找我们可能感兴趣的每个月的第一天和最后一天(*)。 Then, MonthRanges recombines this data with the original ranges and splits them as required so that each period we're dealing with only represents days from a single month. 然后, MonthRanges将此数据与原始范围重新组合,并根据需要对其进行拆分,以便我们处理的每个期间仅代表一个月中的几天。 Then we can just use DATEDIFF to calculate the number of days that each range spans (and add 1 since we're dealing with dates and want inclusive values) 然后,我们可以使用DATEDIFF来计算每个范围的天数(并添加1,因为我们正在处理日期并且需要包含值)

(*) the Months CTE will work provided that we're not dealing with any ranges that span over multiple months and where no other ranges start or end in the intervening months. (*)如果我们不处理跨多个月的任何范围,并且在随后的几个月中没有其他范围开始或结束,则CTE Months将起作用。 If you need to cope with this situation, I need to revise the Months CTE. 如果您需要应对这种情况,我需要修改Months CTE。 Eg if we add ('20120115','20120315') (and no other range) to the above sample, we won't get a result for February using the above. 例如,如果将('20120115','20120315') (而不添加其他范围)添加到上述示例中,则使用上述示例将不会获得2月份的结果。 Do we need to cope with this situation? 我们需要应付这种情况吗?


To cope with the situation noted in (*), we can replace the Months CTE in the above query with: 为了解决(*)中提到的情况,我们可以将上述查询中的Months CTE替换为:

;With LastMonth as (
    select MAX(ToDate) as Mx from @Ranges
), MultiMonths as (
    select
        DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010101') as MonthStart,
        DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010131') as MonthEnd
    from @Ranges
    union all
    select
        DATEADD(month,1,MonthStart),
        DATEADD(month,1,MonthEnd)
    from MultiMonths
    where MonthStart <= (select Mx from LastMonth)
), Months as (
    select distinct MonthStart,MonthEnd from MultiMonths 
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM