MySQL - оптимизация самостоятельного соединения - PullRequest
0 голосов
/ 27 января 2012

У меня есть таблица телефонных событий от HomeId. Каждая строка имеет EventId (на трубку, на трубку, звонок, DTMF и т. Д.), TimeStamp, Sequence (автоинкремент) и HomeId. Я работаю над запросом, чтобы найти конкретные типы вхождений (входящие или исходящие вызовы IE) и продолжительность.

Я планировал сделать это, используя множественное самостоятельное соединение в этой таблице, чтобы выбрать последовательности событий, которые обычно указывают на один тип вхождения или другой. Например, входящие вызовы EG будут периодом бездействия, за которым не будет DTMF, затем вызов и идентификатор вызывающего абонента (возможно), а затем будет снята трубка. Я бы нашел следующую трубку и, таким образом, имел продолжительность.

Моя таблица индексируется по HomeId, EventId и Sequence и имеет ~ 60 тыс. Записей. Когда я делаю «объяснение» моего запроса, он показывает индексирование и число строк 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'

1 Ответ

1 голос
/ 27 января 2012

Полная перезапись на основе новой информации.Я подошел к этому так, чтобы начать с самого внутреннего запроса, чтобы получить все записи, которые нам нужны, основываясь исключительно на HomeID = 111, и убедиться, что они возвращены, предварительно отсортированные по идентификатору последовательности (есть индекс для HomeID, Sequence).Как мы все знаем, телефонный звонок начинается с того, что он берет трубку - eventID = 12, получает тональный сигнал набора - eventid = 22, набирает номер и кто-то отвечает, пока телефон не встанет на крючок - eventid = 30),Если это зависание (eventid = 13), мы хотим его проигнорировать.

Я не знаю, почему вы смотрите последовательность # PRIOR для текущего вызова, не знаю, есть ли она на самом делеподшипник.Похоже, вы просто пытаетесь получить завершенные звонки и как долго продолжительность.Тем не менее, я бы удалил часть LEFT JOIN Phone_Event и соответствующее предложение WHERE.Возможно, это было там, пока вы просто пытались это выяснить.

Во всяком случае, вернемся к логике.Внутреннее большинство гарантирует последовательность вызовов в порядке.У вас не будет двух вызовов одновременно.Поэтому, приведя их в порядок сначала, я присоединяюсь к SQLVars (который создает встроенную переменную @NextCall для запроса).Цель этого состоит в том, чтобы идентифицировать каждый раз, когда начинается новый вызов (EventID = 12).Если это так, возьмите любой порядковый номер и сохраните его.Это будет оставаться неизменным до следующего вызова, поэтому все остальные «идентификаторы событий» будут иметь одинаковые «идентификаторы начальной последовательности».Кроме того, я ищу другие события ... событие = 22 на основе начальной последовательности +1 и установки его в качестве флага.Затем максимальное время, основанное на начале вызова (устанавливается только, когда eventid = 12), и конце вызова (eventid = 30), и, наконец, флаг, основанный на вашей проверке на зависание (eventid = 13), т.е.: не учитывать вызов, если это было зависание и отсутствие соединения через.

Делая группу, я, по сути, свернул каждый вызов в свою собственную линию ... сгруппированную подомашний идентификатор и порядковый номер, используемый для инициации фактического телефонного звонка.Как только это будет сделано, я могу запросить данные и вычислить длительность вызова, поскольку время начала / окончания находится в одной и той же строке, а само-самостоятельное соединение не включено.

Наконец, предложение where ..Выбить любые телефонные звонки, которые были повешены.Опять же, я не знаю, нужен ли вам еще элемент времени начала звонка последнего завершающего события.

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 )

Сообщение о КОММЕНТАРИИ ОТ ОБРАТНОЙ СВЯЗИ / ОШИБКИ

Чтобы попытаться исправить ошибку DOUBLE, вам, очевидно, потребуется внести небольшие изменения в выбор SQLVars.попробуйте следующее

(выберите @NextCall: = CAST (0 в качестве INT)) SQLVars

Теперь, что делает IF () ... Давайте посмотрим.

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

означает, что взгляните на идентификатор события.Если это 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 хранит все последовательные записи для данного вызова "Сгруппированные" вместе, поэтому вы не сможетенужно просто использовать информацию больше, чем span, или меньше ... Она всегда будет следовать определенному пути «событий», поэтому, какой бы ни был тот, который начал вызов, это основа для остальных событий до следующего вызовазапускается, затем последовательность TH захватывается для группового вызова.

Да, это много для понимания ... но, надеюсь, теперь более удобочитаемо для вас:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...