【问题标题】:SQL Server query for total number of days for a month between date rangesSQL Server 查询日期范围之间一个月的总天数
【发布时间】:2012-11-28 07:02:45
【问题描述】:

我有特定的日期范围,比如

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

我想编写一个 SQL 查询来计算两个日期之间的总天数和特定月份的总天数

我想要的输出是,

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

谁能帮我写这个 SQL 查询?

【问题讨论】:

  • 你想在 From Date 和 to Date 之间减去是吗?没有完全得到关于求和的下一个要求
  • 我想找出两个日期之间的差异并总结同一个月的最终结果。 (即)第 1 行相差 6 天,第 2 行 2 天,第 3 行 2 天。现在总天数是 10。但我想按月分组。即,6+2+1(11 个月的 1 天)= 9,另外 1 天属于第 12 个月。

标签: sql sql-server


【解决方案1】:

理想情况下,您有一个名为“日期”的表格,其中包含您将使用的所有日期,例如从 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;

结果:

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

请注意,不要仅将“11”或“12”显示为月份,如果您的范围超过 12 个月,这将无法正常工作,或者在跨过新年时无助于排序,此查询改为显示该月的第一天。

如果没有,您可以根据下面的扩展查询即时虚拟地创建一个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;

此处提供了完整的工作示例:SQL Fiddle

【讨论】:

  • 非常感谢您的回答,它完全符合我的要求
【解决方案2】:

试试这个

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

【讨论】:

    【解决方案3】:

    我会将其分解为几个步骤(每个步骤都有一个单独的 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)
    

    首先,Months CTE 查找我们可能感兴趣的每个月的第一天和最后几天 (*)。然后,MonthRanges 将此数据与原始范围重新组合,并根据需要对其进行拆分,以便我们处理的每个时间段仅代表一个月中的几天。然后我们可以只使用DATEDIFF 来计算每个范围跨越的天数(并添加 1,因为我们正在处理日期并想要包含值)

    (*) Months CTE 将起作用,前提是我们不处理跨越多个月的任何范围,并且在中间几个月内没有其他范围开始或结束。如果您需要应对这种情况,我需要修改Months CTE。例如。如果我们在上面的示例中添加('20120115','20120315')(并且没有其他范围),我们将不会使用上面的方法得到二月份的结果。我们需要应对这种情况吗?


    为了应对 (*) 中指出的情况,我们可以将上述查询中的 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 
    )
    

    【讨论】:

      【解决方案4】:

      请原谅我写得不好的 SQL。
      假设是月份差异。 fromdate 和 todate 之间是 1。

      架构

      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

      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
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-06
        • 2011-08-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-15
        • 1970-01-01
        相关资源
        最近更新 更多