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

时间:2020-11-30 06:31:56

Is there an easy way to do this? By fully between, I mean don't count the 7am or 7pm datetimes that are equal to the start or end time.

是否有捷径可寻?完全介于两者之间,我的意思是不要将7am或7pm的日期时间计算为等于开始或结束时间。

I imagine this can be done using the unix timestamp in seconds and a bit of algebra, but I can't figure it out.

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

I'm happy to use something in PLSQL or plain SQL.

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

Examples:

例子:

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 个解决方案

#1


3  

I think this could be reduced further but I don't have Oracle at my disposal to completely test this Oracle SQL:

我认为这可以进一步减少,但我没有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;

Thanks to @A.B.Cade for the Fiddle, it looks like my CASE Logic can be condensed further to:

感谢@ 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;

#2


3  

I had fun writing the following solution:

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

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;

I added an id column to the table in case the range of dates aren't unique.

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

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

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

#3


2  

This may not have the best performance but might work for you:

这可能没有最佳性能,但可能适合您:

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

UPDATE: As to @FlorinGhita's comment - fixed the query to include zero occurences

更新:至于@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

#4


1  

Do like this (in SQL)

这样做(在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 

#1


3  

I think this could be reduced further but I don't have Oracle at my disposal to completely test this Oracle SQL:

我认为这可以进一步减少,但我没有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;

Thanks to @A.B.Cade for the Fiddle, it looks like my CASE Logic can be condensed further to:

感谢@ 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;

#2


3  

I had fun writing the following solution:

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

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;

I added an id column to the table in case the range of dates aren't unique.

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

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

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

#3


2  

This may not have the best performance but might work for you:

这可能没有最佳性能,但可能适合您:

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

UPDATE: As to @FlorinGhita's comment - fixed the query to include zero occurences

更新:至于@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

#4


1  

Do like this (in SQL)

这样做(在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