SQL Server:如何对按日/小时等分组的多个日期时间范围执行计数?

时间:2015-06-10 00:32:17

标签: sql sql-server

我试图为SQL Server编写一些SQL代码。以下是原始数据的示例:

CREATE TABLE TimeTable
(
  id int,
  startDate Datetime,
  endDate Datetime
);

INSERT INTO TimeTable (id, startDate, endDate)
VALUES
(1, '2015/06/01', '2015/06/10'),
(2, '2015/06/03', '2015/06/10'),
(3, '2015/06/05', '2015/06/10'),
(4, '2015/06/03', '2015/06/06'),
(5, '2015/06/01', '2015/06/03');

视觉上数据如下所示:

2015/06/01         |   |        
2015/06/02         |   |           
2015/06/03         || ||          
2015/06/04         || |        
2015/06/05         ||||        
2015/06/06         ||||            
2015/06/07         |||               
2015/06/08         |||                
2015/06/09         |||              
2015/06/10         |||               

这是我想在下面输出的格式(但也可以按小时分组)。

DateByDay  CountOnDay
2015/06/01 2
2015/06/02 2
2015/06/03 4
2015/06/04 3
2015/06/05 4
2015/06/06 4
2015/06/07 3
2015/06/08 3
2015/06/09 3
2015/06/10 3

=============================================== ======================== 多谢你们! 我已经重写了它,因为我现在理解生成一个日期时间范围并对数据进行连接。我已经重新设计了这个时间来考虑小时数。

CREATE TABLE TimeTable
(
  id int,
  startDate Datetime,
  endDate Datetime
);

INSERT INTO TimeTable
(id, startDate, endDate)
VALUES
(1, '2015/06/01 01:30', '2015/06/01 07:00'), --FRINGE CASE since 01:30 should still be at June, 01 2015 01:00:00
(2, '2015/06/01 02:00', '2015/06/01 07:00'),
(3, '2015/06/01 03:00', '2015/06/01 07:00'),
(4, '2015/06/01 04:00', '2015/06/01 07:00'),
(5, '2015/06/01 05:00', '2015/06/01 07:00'),
(8, '2015/06/01 06:00', '2015/06/01 07:00');

DECLARE @From DATETIME, @To DATETIME
SET @From = '2015-06-01 00:00:00'
SET @To = '2015-06-02 20:00:00'

SELECT DateHour, count(B.id)
FROM 
(
  SELECT DATEADD(HOUR,number,@From) DateHour
  FROM master..spt_values
  WHERE type = 'P'
  AND DATEADD(HOUR,number,@From) <= @To
) A
LEFT JOIN TimeTable B
ON DateHour BETWEEN startDate AND endDate
GROUP BY DateHour

http://sqlfiddle.com/#!3/7c36e/2

然而01:30的边缘情况应该出现在凌晨1点。 什么是实现这种条件的最佳方式

DateHour BETWEEN startDate AND endDate

从上午01:00起不起作用不在01:30和07:00之间

3 个答案:

答案 0 :(得分:3)

如果你没有datetable,但知道你想要的月份,你可以使用(我从here得到这个想法):

declare @month int = 6

;With DateTable as (
  select distinct dateadd(month,@month-1,dateadd(year,datediff(year,0,getdate()),0))+number DayOfMonth
  from master..spt_values A
  where number >= 0 and number < day(dateadd(month,@month,0)-1)
)

select DayOfMonth, count(B.id)
from DateTable A
left join TimeTable B
  on DayOfMonth between startDate and endDate
group by DayOfMonth

查看Fiddle

答案 1 :(得分:2)

所以,假设你有某种日期表,那很简单:

select
d.thedate,
count (case when d.thedate between t.startdate and t.enddate then 1 else null end)
from
TimeTable t
inner join datetable d
on d.thedate between t.startdate and t.enddate
group by 
d.thedate

SQL Fiddle

如果您没有日期表(创建一个!),您可以使用递归CTE来构建最早的开始日期和最新结束日期之间的日期列表:

with dateCTE
as (
select min(startdate) as thedate,
 max(enddate) as maxdate
from
timetable
union all
select thedate + 1,
  maxdate
from datecte
  where thedate + 1 <= datecte.maxdate
)
select
d.thedate,
count (case when d.thedate between t.startdate and t.enddate then 1 else null end)
from
TimeTable t
inner join datecte d
on d.thedate between t.startdate and t.enddate
group by 
d.thedate

Fiddle using CTE

答案 2 :(得分:0)

以下是直接为表格中的值自定义的版本。表master..spt_values是一个方便的数字来源。如果您没有日历表,这将非常有用。

with dates as (
      select dateadd(day, n.n, d1) as dte
      from (select row_number() over (select NULL) - 1 as n
            from master..spt_values n
           ) n join
           (select min(startdate) as d1, max(enddate) as d2
            from TimeTable
           ) tt
           on dateadd(day, n.n, d1) <= d2
     )
select d.dte,
       (select count(*)
        from TimeTable tt
        where d.dte between tt.startdate and tt.enddate
       ) as cnt
from dates d ;

CTE计算数据表示的所有日期(从最小开始日期到最大结束日期)。外部查询然后计算每个日期的匹配记录数。