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