2012-01-26 255 views
0

我有一个HomeId的手机事件表。每行都有一个EventId(挂机,摘机,响铃,DTMF等),TimeStamp,Sequence(自动递增)和HomeId。我正在查询查找特定类型的事件(IE入站或出站呼叫)和持续时间。MySQL - 自我连接优化

我已经计划在此表上使用多重自联接来选择通常指示一种类型的事件或其他事件的事件序列。 EG入站呼叫将处于非活动时段,随后是非DTMF,然后响铃和呼叫者ID(可能)然后摘机。我会发现下一个挂机,因此有持续时间。

我的表由HomeId,EventId和Sequence索引,有约60K条记录。当我对查询进行“解释”时,会显示索引,并显示行数75,75,1,1,748。看起来非常可行。但是当我运行查询时,需要超过10分钟(此时MySQL查询浏览器超时)。

查询外呼:

select pe0.HomeId, pe1.Stamp, pe1.mSec, timediff(pe4.Stamp, pe0.Stamp) from Phone_Events pe0 
join Phone_Events pe1 on pe0.HomeId = pe1.HomeId and pe1.Sequence = pe0.Sequence - 1 and abs(timediff(pe0.Stamp, pe1.Stamp)) > 10 
join Phone_Events pe2 on pe0.HomeId = pe2.HomeId and pe2.Sequence = pe0.Sequence + 1 and pe2.EventId = 22 
join Phone_Events pe4 on pe4.HomeId = pe0.HomeId and pe4.EventId = 30 and pe4.Stamp > pe0.Stamp 
where pe0.eventId = 12 and pe0.HomeId = 111 
AND 
    NOT EXISTS(SELECT * FROM Phone_Events pe3 
       WHERE pe3.HomeId = pe0.HomeId 
       AND pe3.EventId not in(13, 22) 
       AND pe3.Stamp > pe0.Stamp and pe3.Stamp < pe4.Stamp); 

有没有具体的自我加入,使这种缓慢的东西吗?有没有更好的方法来优化这个?杀手似乎是'不存在'部分 - 这部分是为了确保在最后'挂机'和当前'摘机'之间没有事件发生。

编辑:EVENTID的如下:

'1', 'device connection' 
'2', 'device disconnection' 
'3', 'device alarm' 
'11', 'ring start' 
'12', 'off hook' 
'13', 'hang up(other end)' 
'15', 'missed call' 
'21', 'caller id' 
'22', 'dtmf' 
'24', 'device error' 
'30', 'on hook' 
'31', 'ring stop' 
+0

看起来像邮票的指标将有助于 – Randy

+0

的主键(序列,首页)。还有(Sequence,Home),EventId和HomeId的索引。 – ethrbunny

+0

abs(timediff(pe0.Stamp,pe1.Stamp))< - 这对于性能不是很好。 – JohnFx

回答

1

完全重写基于新的信息。我是如何处理这个问题的,首先是查询最内层的查询,以便根据HomeID = 111获取我们关心的所有记录,并确保它们按照序列ID(HomeID,Sequence上的索引)进行了预先排序。众所周知,打电话是通过接电话开始的 - eventID = 12,获得拨号音 - eventid = 22,拨出电话,有人接听电话,直到电话重新开机 - eventid = 30) 。如果它是一个挂断(eventid = 13),我们想忽略它。

我不知道你为什么要看序列#在当前调用之前,不知道它是否真的有方位。它看起来像你只是试图完成呼叫,持续时间有多长。也就是说,我将删除LEFT JOIN Phone_Event的部分和相应的WHERE子句。它可能在那里,而你只是想弄明白这一点。不管怎样,回到逻辑上。最内在保证呼叫顺序。您不会同时拨打两个电话。因此,通过首先获取它们,然后加入SQLVars(为查询创建内联变量@NextCall)。其目的是为了识别每次新的呼叫即将开始(EventID = 12)。如果是这样,请记下序号,并保存。这将保持不变,直到下一次呼叫,所有其他“事件ID”将具有相同的“起始序列ID”。另外,我正在寻找其他事件......事件= 22,根据启动顺序+1并将其设置为标志。然后,基于呼叫开始的最大时间(只在eventid = 12时设置)和呼叫结束(eventid = 30),最后是根据您的检查挂断(eventid = 13)的标志,即:如果是挂断电话而没有连接,则不要考虑通话。

通过分组,我本质上已经将每个呼叫汇总到自己的线路中......按家庭ID和用于发起实际电话呼叫的序列号进行分组。一旦THAT完成,我可以查询数据并计算自开始/结束时间在同一行以来的通话持续时间,而不涉及自我自我加入。

最后,where子句...踢出任何有HANG UP的电话。再一次,我不知道你是否仍然需要开始呼叫的时间是最后结局事件的元素。

SELECT 
     PreGroupedCalls.*, 
     timediff(PreGroupedCalls.CallEndTime, PreGroupedCalls.CallStartTime) CallDuration 
    from 
     (SELECT 
       Calls.HomeID, 
       @NextCall := @NextCall + if(Calls.EventID = 12, Calls.Sequence, @NextCall) as NextNewCall, 
       MAX(if(Calls.EventID = 12, Calls.Stamp, 0)) as CallStartTime, 
       MAX(if(Calls.EventID = 30, Calls.Stamp, 0)) as CallEndTime, 
       MAX(if(Calls.EventID = 22 and Calls.Sequence = @NewCallFirstSeq +1, 1, 0)) as HadDTMFEntry, 
       MAX(if(Calls.EventID = 13 and Calls.Sequence = @NewCallFirstSeq +1, 1, 0)) as WasAHangUp 
      from 
       (select pe.HomeId, 
         pe.Sequence, 
         pe.EventID, 
         pe.Stamp 
        from 
         Phone_Events pe 
        where 
         pe.HomeID = 111 
        order by 
         pe.Sequence) Calls, 
       (select @NextCall := 0) SQLVars 
      group by 
       Calls.HomeID, 
       NextNewCall) PreGroupedCalls 

     LEFT JOIN Phone_Event PriorCallEvent 
      ON PreGroupCalls.NextNewCall = PriorCallEvent.Sequence -1 
    where 
     PreGroupedCalls.WasHangUp = 0 
     AND (PriorCallEvent.Sequence IS NULL 
     OR abs(timediff(PriorCallEvent.Stamp, PreGroupedCalls.CallStartTime)) > 10) 

评论作者反馈/报告的错误

尝试并解决了双重错误,你显然需要作出的SQLVars略有变化选择..请尝试以下

(选择@NextCall:= CAST(0作为INT))SQLVars

现在,IF()正在做什么......让我们来看看。

@NextCall +如果(Calls.EventID = 12,Calls.Sequence,@NextCall)

装置看一看的事件ID。如果它是12(即:摘机),则抓取该条目的序列号。这将成为另一个呼叫的新“起始序列”。如果没有,只要保留最后设置的值即可,因为它是正在进行的呼叫的延续。现在,让我们看看一些模拟数据,以帮助更好地说明所有列

Original data     Values that will ultimately be built into... 
HomeID Sequence EventID Stamp @NextCall 
111 1  12  8:00:00 1 beginning of a new call 
111 2  22  8:00:01 1 not a new "12" event, keep last value 
111 3  30  8:05:00 1 call ended, phone back on hook 
111 4  12  8:09:00 4 new call, use the sequence of THIS entry 
111 5  22  8:09:01 4 same call 
111 6  13  8:09:15 4 same call, but a hang up 
111 7  30  8:09:16 4 same call, phone back on hook 
111 8  12  8:15:30 8 new call, get sequence ID 
111 9  22  8:15:31 8 same call... 
111 10  30  8:37:15 8 same call ending... 

Now, the query SHOULD create something like this 
HomeID NextNewCall CallStartTime CallEndTime HadDTMFEntry WasAHangUp 
111  1    8:00:00  8:05:00  1    0 
111  4    8:09:00  8:09:16  1    1 
111  8    8:15:30  8:37:15  1    0 

正如你所看到的,@NextCall保持所有针对给定的叫“群”的顺序条目在一起,所以你不必只是使用大于跨度信息或小于...它总是遵循一定的“事件”路径,所以无论是什么时候开始呼叫都是其余事件的基础,直到下一次呼叫开始,那么这个序列就被抓到了这个组呼。

是啊,它有很多的把握..但希望现在更易消化为你:)

+0

嗯..我添加了新的索引并运行了你的版本。仍然击中10分钟超时。有问题的服务器是8cpu,16Gb RAM RHEL机器,没有其他负载(目前)。看起来像这样应该快得多。 – ethrbunny

+0

@ethrbunny,让我问你这个......“HomeID”是否也是自动分配的?这是电话发起的电话分机吗?时间戳范围...您期望的时间限制是否有限制?你可以用你的查询发布一些样本数据吗? – DRapp

+0

@ethrbunny,你可以列出不同的事件ID和他们相应的代码...我有一个想法可能工作...此外,你有一个时间戳,但查询整个文件...任何考虑LIMIT给定的时间段,例如一天,一周,一个月? – DRapp