计算两个日期时间之间的7AM或7PM事件的数量

时间:2012-11-14 06:23:50

标签: sql oracle plsql oracle11g

有一种简单的方法吗?完全介于两者之间,我的意思是不要将7am或7pm的日期时间计算为等于开始或结束时间。

我想这可以使用unix时间戳(以秒为单位)和一些代数来完成,但我无法弄明白。

我很高兴在PLSQL或纯SQL中使用某些东西。

示例:

start             end               num_7am_7pm_between_dates
2012-06-16 05:00  2012-06-16 08:00  1  
2012-06-16 16:00  2012-06-16 20:00  1
2012-06-16 05:00  2012-06-16 07:00  0
2012-06-16 07:00  2012-06-16 19:00  0
2012-06-16 08:00  2012-06-16 15:00  0
2012-06-16 05:00  2012-06-16 19:01  2
2012-06-16 05:00  2012-06-18 20:00  6 

4 个答案:

答案 0 :(得分:3)

我认为这可以进一步减少,但我没有Oracle可以完全测试这个Oracle SQL:

SELECT StartDate
     , EndDate
     , CASE WHEN TRUNC(EndDate) - TRUNC(StartDate) < 1
             AND TO_CHAR(EndDate, 'HH24') > 19
             AND TO_CHAR(StartDate, 'HH24') < 7
            THEN 2
            WHEN TRUNC(EndDate) - TRUNC(StartDate) < 1
             AND (TO_CHAR(EndDate, 'HH24') > 19
              OR  TO_CHAR(StartDate, 'HH24') < 7)
            THEN 1
            WHEN TRUNC(EndDate) - TRUNC(StartDate) > 0
             AND TO_CHAR(EndDate, 'HH24') > 19
             AND TO_CHAR(StartDate, 'HH24') < 7
            THEN 2 + ((TRUNC(EndDate) - TRUNC(StartDate)) * 2)
            WHEN TRUNC(EndDate) - TRUNC(StartDate) > 0
             AND TO_CHAR(EndDate, 'HH24') > 19
              OR TO_CHAR(StartDate, 'HH24') < 7
            THEN 1 + ((TRUNC(EndDate) - TRUNC(StartDate)) * 2)
            ELSE 0
       END
FROM MyTable;

感谢@ A.B.Cade为小提琴,看起来我的CASE Logic可以进一步浓缩到:

SELECT SDate
     , EDate
     , CASE WHEN TO_CHAR(EDate, 'HH24') > 19
             AND TO_CHAR(SDate, 'HH24') < 7
            THEN 2 + ((TRUNC(EDate) - TRUNC(SDate)) * 2)
            WHEN TO_CHAR(EDate, 'HH24') > 19
              OR TO_CHAR(SDate, 'HH24') < 7
            THEN 1 + ((TRUNC(EDate) - TRUNC(SDate)) * 2)
            ELSE 0
       END AS MyCalc2
  FROM MyTable;

答案 1 :(得分:3)

我很乐意编写以下解决方案:

with date_range as (
  select min(sdate) as sdate, max(edate) as edate
  from t
),
all_dates as (
  select sdate + (level-1)/24 as hour
  from date_range
  connect by level <= (edate-sdate) * 24 + 1
),
counts as (
  select t.id, count(*) as c
  from all_dates, t
  where to_char(hour, 'HH') = '07'
  and hour > t.sdate and hour < t.edate
  group by t.id
)
select t.sdate, t.edate, nvl(counts.c, 0)
from t, counts
where t.id = counts.id(+)
order by t.id;

我在表中添加了一个id列,以防日期范围不唯一。

http://www.sqlfiddle.com/#!4/5fa19/13

答案 2 :(得分:2)

这可能没有最好的表现但可能适合你:

select sdate, edate, count(*)
  from (select distinct edate, sdate, sdate + (level / 24) hr
          from t
        connect by sdate + (level / 24) <= edate )
 where to_char(hr, 'hh') = '07'
group by sdate, edate

更新:关于@ FlorinGhita的评论 - 修正了查询以包括零次出现

select sdate, edate, sum( decode(to_char(hr, 'hh'), '07',1,0))
  from (select distinct edate, sdate, sdate + (level / 24) hr
          from t
        connect by sdate + (level / 24) <= edate )
group by sdate, edate

答案 3 :(得分:1)

这样做(在SQL中)

declare  @table table ( start datetime, ends datetime) 
insert into @table select'2012-06-16 05:00','2012-06-16 08:00'  --1  
insert into @table select'2012-06-16 16:00','2012-06-16 20:00'  --1
insert into @table select'2012-06-16 05:00','2012-06-16 07:00'  --0
insert into @table select'2012-06-16 07:00','2012-06-16 19:00'  --0
insert into @table select'2012-06-16 08:00','2012-06-16 15:00'  --0
insert into @table select'2012-06-16 05:00','2012-06-16 19:01'  --2
insert into @table select'2012-06-16 05:00','2012-06-18 20:00'  --6 
insert into @table select'2012-06-16 07:00','2012-06-18 07:00'  --3


Declare @From DATETIME 
Declare @To DATETIME 

select @From = MIN(start) from @table 
select @To = max(ends) from @table 

;with CTE AS 
( 
      SELECT distinct 
    DATEADD(DD,DATEDIFF(D,0,start),0)+'07:00' AS AimTime
    FROM @table 
 ),CTE1 AS 
 ( 
    Select AimTime
    FROM CTE 

    UNION ALL 

    Select DATEADD(hour, 12, AimTime)
From CTE1 
WHERE AimTime< @To 
 )


 select start,ends, count(AimTime)
 from CTE1 right join @table t 
 on t.start < CTE1.AimTime and t.ends > CTE1.AimTime
 group by start,ends