SQL汇总时间范围的最小/最大活动时间

时间:2008-11-19 12:07:00

标签: sql

这是一个SQL问题,我无法在一个简单的查询中解决这个问题吗?

数据集是(为便于理解而添加的字母):

Start      End
10:01      10:12     (A)
10:03      10:06     (B)
10:05      10:25     (C)
10:14      10:42     (D)
10:32      10:36     (E)

所需的输出是:

PeriodStart   New       ActiveAtEnd    MinActive   MaxActive
09:50         0         0              0           0
10:00         3 (ABC)   2 (AC)         0           3 (ABC)
10:10         1 (D)     2 (CD)         1 (C)       2 (AC or CD)
10:20         0         1 (D)          1 (C)       2 (CD)
10:30         1 (E)     1 (D)          1 (D)       2 (DE)
10:40         0         0              0           1 (D)
10:50         0         0              0           0

因此,所需的查询是第一个表的摘要,计算10分钟内第一个表中的最小重叠时间段(开始 - 结束)和最大重叠时间段(开始 - 结束)。

'新'是摘要期间带有“开始”的行数。 'ActiveAtEnd'是摘要周期结束时活动的行数。

我正在使用Oracle,但我确信可以调整解决方案。不允许存储过程 - 只需简单的SELECT / INSERT(允许视图)。每10分钟输出一次运行一个SQL命令也是可以的(一旦填充,这将是它保持最新的方式。

感谢任何想法,包括“不可能”; - )

4 个答案:

答案 0 :(得分:3)

假设您还拥有(或创建)一个名为@Times的表,每个十分钟的开始时间有一条记录, 怎么样......

   Select T.Start,  
      (Select Count(*) From testTab
       Where Start Between T.Start 
          And DateAdd(minute, 10, T.Start)) New,
      (Select Count(*) From testTab
       Where Start < DateAdd(minute, 10, T.Start)
          And EndDt > DateAdd(minute, 10, T.Start)) ActiveAtEnd,
      (Select Max(Cnt) From 
          (Select Count(Distinct T.Which) Cnt
           From  (Select Distinct Start
                    From testTab
                    Where Start Between T.Start 
                           And DateAdd(minute, 10, T.Start)
                    Union Select T.Start 
                    Union Select DateAdd(minute, 10, T.Start)) Z
             Left Join testTab T 
               On Z.Start Between T.Start And T.EndDt
           Group By Z.Start) ZZ ) MaxActive,
      (Select Min(Cnt) From 
           (Select Count(Distinct T.Which) Cnt
            From  (Select Distinct Start
                   From testTab
                   Where Start Between T.Start 
                           And DateAdd(minute, 10, T.Start)
                   Union Select T.Start                                                         
                   Union Select DateAdd(minute, 10, T.Start)) Z
              Left Join testTab T 
                 On Z.Start Between T.Start And T.EndDt
           Group By Z.Start) ZZ ) MinActive     
   From @Times T

我使用

在SQL Server中将此表创建为Table变量
Declare @Times Table (Start datetime Primary key Not Null)
Declare @Start DateTime 
Set @Start = '1 Nov 2008 10:00'
While @Start < '1 Nov 2008 11:00' begin
    Insert @Times(Start) values(@Start)
    Set @Start = DateAdd(minute, 10, @Start) 
End

如果您正在使用其他产品,请使用临时表...但是这种方法确实需要一个表,每10分钟“句点”有一条记录作为对...运行的钩子。

使用以下数据,此查询按如下方式生成输出:

start                   endDt                   Which
----------------------- ----------------------- -----
2008-11-01 10:01:00.000 2008-11-01 10:12:00.000 A
2008-11-01 10:03:00.000 2008-11-01 10:06:00.000 B
2008-11-01 10:05:00.000 2008-11-01 10:25:00.000 C
2008-11-01 10:14:00.000 2008-11-01 10:42:00.000 D
2008-11-01 10:32:00.000 2008-11-01 10:36:00.000 E
2008-11-01 10:22:00.000 2008-11-01 10:51:00.000 F
2008-11-01 10:22:00.000 2008-11-01 10:23:00.000 G

Start                   New         ActiveAtEnd MaxActive   MinActive
----------------------- ----------- ----------- ----------- -----------
2008-11-01 10:00:00.000 3           2           3           0
2008-11-01 10:10:00.000 1           2           2           2
2008-11-01 10:20:00.000 2           2           4           2
2008-11-01 10:30:00.000 1           2           3           2
2008-11-01 10:40:00.000 0           1           2           1
2008-11-01 10:50:00.000 0           0           1           0

警告:聚合或其他SET操作消除了空值。

答案 1 :(得分:1)

我正在努力使用ActiveAtEnd值,但其他人都没问题。

这是针对MySQL:

set @active:=0;

select 
  period, 
  sum( if( score=1, 1, 0)) New, 
  if( max(ab) > max(aa), max(ab), max(aa)) MaxActive, 
  if( min( ab ) < min( aa ), min(ab), min(aa)) MinActive  
from  (
        select 
          period, 
          etime, 
          score, 
          @active ab, 
          @active:=@active+score aa 
        from (
               select 
                 from_unixtime( floor( unix_timestamp(start)/600) * 600) period, 
                 start etime, 
                 +1 score 
               from ev  
               union all
               select from_unixtime( floor( unix_timestamp(end)/600) * 600) period, 
                 end etime, 
                 -1 score
               from ev 
             ) event order by etime
      ) as temp 
group by period;

最里面的选择将原始表分成一组事件 - 开始事件的得分为+1,结束事件的得分为-1。使用union all以便允许重复事件。

下一个内部选择在分数值上运行变量 - @active保存每个时间点的活动间隔数。选择添加当前计数之前和之后的@active值:我不知道这是多么便携。

最外面的选择累积每个时期的结果。 “新”是“+1”分数的总和,MaxActive和MinActive必须同时考虑(ab)之前的活动值和(aa)之后的活动值。

以下是样本结果:

+---------------------+------+-----------+-----------+
| period              | New  | MaxActive | MinActive |
+---------------------+------+-----------+-----------+
| 2008-11-19 10:00:00 |    3 |         3 |         0 |
| 2008-11-19 10:10:00 |    1 |         2 |         1 |
| 2008-11-19 10:20:00 |    0 |         2 |         1 |
| 2008-11-19 10:30:00 |    1 |         2 |         1 |
| 2008-11-19 10:40:00 |    0 |         1 |         0 |
+---------------------+------+-----------+-----------+

答案 2 :(得分:0)

New和ActiveAtEnd相当简单(假设句点的开头和结尾存储在临时变量中):

select  @periodStart PeriodStart
, @periodEnd PeriodEnd 
, n.[new]
, ae.ActiveAtEnd
from (
select  count(*) [new] 
from    @times 
where   [start] >= @periodStart
and [start] < @PeriodEnd 
) n 
cross join 
(
select  count(*) [ActiveAtEnd] 
from    @times
where   [start] < @PeriodEnd 
and [end] >= @PeriodEnd 
) ae

Max和Min Actives更难。您可以设定一分钟的粒度,因此您需要以该粒度分解活动期以便能够探测每个切片。

我不确定在单个查询中是否可行。

答案 3 :(得分:0)

我能够解决这类问题的唯一方法就是每分钟计算一次“开始”。然后,您将获得10分钟组的最大值(或最小值)。我无法应用基于集合的方法。