2013-06-27 2 views
0

У меня есть журнал вызовов для моего торгового представителя Vidget. Каждый клик в записи клиента регистрируется. Сотрудники могут получать доступ к одной и той же учетной записи клиента несколько раз в день, поэтому в разное время дня могут быть десятки последовательных кликов, сгруппированных под одним и тем же идентификатором записи.Получить длительность последовательных представлений записей

Пример:

recordID userID date_event 
33450 321  2013-06-20 16:22:02 
33450 321  2013-06-20 16:22:02 
33450 321  2013-06-20 16:22:24 
33450 321  2013-06-20 16:22:24 
22222 321  2013-06-20 16:22:53 
22222 321  2013-06-20 16:22:54 
12345 321  2013-06-20 16:23:43 
12345 321  2013-06-20 16:23:44 
12345 321  2013-06-20 16:24:00 
12345 321  2013-06-20 16:24:05 
12345 321  2013-06-20 16:24:05 
12345 321  2013-06-20 18:16:09 
12345 321  2013-06-20 18:16:09 
33450 321  2013-06-20 18:33:24 
33450 321  2013-06-20 18:35:11 
33450 321  2013-06-20 18:36:55 
12345 321  2013-06-20 19:01:14 
98765 321  2013-06-20 19:02:43 

В наборе данных выше, я бы 6 групп доступа.

 first    last     duration(seconds) 
33450 2013-06-20 16:22:02 2013-06-20 16:22:24  22 
22222 2013-06-20 16:22:30 2013-06-20 16:22:54  24 
12345 2013-06-20 16:23:43 2013-06-20 18:16:09  6746 
33450 2013-06-20 18:33:24 2013-06-20 18:36:55  211 
12345 2013-06-20 19:01:14 2013-06-20 19:01:14  0 
98765 2013-06-20 19:02:43 2013-06-20 19:02:43  0 

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

Структура таблицы:

CREATE TABLE IF NOT EXISTS `record_log` (
    `event_id` int(11) NOT NULL AUTO_INCREMENT, 
    `userID` int(5) DEFAULT NULL, 
    `recordID` int(10) DEFAULT NULL, 
    `date_event` datetime DEFAULT NULL, 
    PRIMARY KEY (`event_id`), 
    KEY `userID` (`userID`), 
    KEY `date_event` (`date_event`), 
    KEY `recordID` (`recordID`), 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 

Что бы быть лучшим способом структурировать запрос, возвращающий второй набор данных? Можно ли это сделать в одном запросе, не используя слишком много циклов? У меня было бы потенциально тысячи зарегистрированных записей в определенный день.

+0

У вас есть идентификатор auto_increment? как ваши записи группируются вместе? группа изменяется при изменении идентификатора записи? – fthiella

+0

Да - auto_increment является "event_id". Записи будут сгруппированы по идентификатору записи, но уловка в том, что мне нужно больше, чем просто общая продолжительность всей записи для каждого идентификатора записи за данный период времени. Один и тот же идентификатор записи можно получить несколько раз в течение дня. Как правило, будут группы последовательной деятельности. Или, например, в течение дня может быть доступен только идентификатор записи. Я попытался объяснить это и показать это в моих данных выше. –

ответ

1

Да, можно вернуть указанный набор результатов, но это не очень. В частности, использование встроенных представлений (MySQL называет их «производными таблицами») означает, что строки будут записаны во временную таблицу MyISAM, и эта операция может быть дорогостоящей для больших наборов, поэтому для получения производительности с использованием этого подхода будет вероятно, потребуют некоторые предикаты по самому внутреннему запросу, чтобы получить набор данных с разумным размером ... например

WHERE q.userID = 321 
     AND q.date_event >= '2012-01-01' 
     AND q.date_event < '2012-01-02' 

Примечание: Похоже, что значение «длительности» должно быть ограничено ... то есть вы хотите щелчок в пятницу в 6 вечера, чтобы быть «подобраны» на щелчок в понедельник в 8 утра, это должно быть считается частью «продолжительности». В нижеприведенном запросе я задал максимальное значение продолжительности 6800 секунд, так что 6800 - это самая большая продолжительность, которую можно было бы вернуть, все, что было бы больше, чем это, делится на две длительности.

Вот пример запроса, который возвращает указанный результирующий:

SELECT recordID 
    -- , s.userID 
    , s.first 
    , MAX(s.date_event) AS `last` 
    , MAX(TIMESTAMPDIFF(SECOND,s.first,s.date_event)) AS duration 
    -- , MAX(s.cnt) AS `cnt` 
    FROM ( 
     SELECT IF(r.recordID = @record_id AND r.userID = @user_id AND r.date_event < (@date_event + INTERVAL 6800 SECOND), 
       @cnt := @cnt + 1, @cnt := 1) AS `cnt` 
       , IF(r.recordID = @record_id AND r.userID = @user_id AND r.date_event < (@date_event + INTERVAL 6800 SECOND), 
       @first, @first := r.date_event) + INTERVAL 0 SECOND AS `first` 
       , @record_id := r.recordID AS recordID 
       , @user_id := r.userID AS userID 
       , @date_event := r.date_event AS date_event 
      FROM (SELECT @record_id := NULL, @user_id := NULL, @date_event := NULL, @cnt := 0, @first := NULL) i 
      JOIN (SELECT q.recordID, q.userID, q.date_event 
        FROM record_log q 
        ORDER BY q.userID, q.date_event, q.recordID 
       ) r 
     ) s 
GROUP 
    BY s.first 
    , s.userID 
    , s.recordID 
ORDER 
    BY s.first 
    , s.userID 
    , s.recordID 

Примечание: этот запрос предполагает, что «длительность» на одну запись будет «разбита» на «продолжительность» на другую запись. (Если пользователь нажимает на запись, а затем нажимает на другой записи, а затем возвращается к исходной записи еще на несколько щелчков мыши, щелчки на оригинальной записи будут посчитаны как два отдельных длительностей.


выборка данных:

INSERT INTO record_log (recordID, userID, date_event) VALUES 
('33450','321','2013-06-20 16:22:02') 
,('33450','321','2013-06-20 16:22:02') 
,('33450','321','2013-06-20 16:22:24') 
,('33450','321','2013-06-20 16:22:24') 
,('22222','321','2013-06-20 16:22:53') 
,('22222','321','2013-06-20 16:22:54') 
,('12345','321','2013-06-20 16:23:43') 
,('12345','321','2013-06-20 16:23:44') 
,('12345','321','2013-06-20 16:24:00') 
,('12345','321','2013-06-20 16:24:05') 
,('12345','321','2013-06-20 16:24:05') 
,('12345','321','2013-06-20 18:16:09') 
,('12345','321','2013-06-20 18:16:09') 
,('33450','321','2013-06-20 18:33:24') 
,('33450','321','2013-06-20 18:35:11') 
,('33450','321','2013-06-20 18:36:55') 
,('12345','321','2013-06-20 19:01:14') 
,('98765','321','2013-06-20 19:02:43') 
+0

Быстрое наблюдение. Зачем использовать 'q.date_event> = '2012-01-01' И q.date_event <'2012-01-02'' вместо' DATE (q.date_event) =' 2012-01-01''? –

+1

Хороший вопрос. В предикате (предложение WHERE) мы не хотим обертывать столбец в функцию, потому что выполнение этого запрещает использование индекса для столбца для удовлетворения запроса. С диапазоном на голом столбце, который может использовать индекс. Но если мы используем 'DATE (q.date_event)', то это фактически заставляет MySQL оценивать выражение (т. Е. Вызывать функцию DATE) для КАЖДОЙ строки в таблице. (На самом деле это самый худший случай: если строки сначала отфильтрованы другим предикатом, тогда MySQL может пропустить оценку выражения для уже отфильтрованных строк.) – spencer7593

0

Я хотел бы использовать запрос SQL с переменными:

SELECT 
    recordID, 
    userID, 
    MIN(date_event) first, 
    MAX(date_event) last, 
    TIME_TO_SEC(TIMEDIFF(MAX(date_event), MIN(date_event))) sec 
FROM (
    SELECT 
    events.*, 
    CASE WHEN @last_recordID=recordID THEN @grp ELSE @grp:[email protected]+1 END groupID, 
    @last_recordID := recordID 
    FROM 
    events, (SELECT @grp:=0, @last_recordID:=NULL) r 
    ORDER BY 
    event_ID 
) s 
GROUP BY 
    recordID, 
    userID, 
    groupID 
ORDER BY 
    groupID 

Пожалуйста, см скрипку here

.
0

Самый простой запрос, чтобы вернуть ваш набор данных будет:

SELECT recordID, MIN(date_event) AS `first`, MAX(date_event) AS `last` 
, TIMESTAMPDIFF(SECOND, MIN(date_event), MAX(date_event)) AS `duration(seconds)` 
FROM `record_log` 
GROUP BY recordID 

Другой вариант, который, вероятно, будет быстрее, чтобы просто выяснить разумную оценку длительности для каждого времени запись о клиенте доступ.Запрос ниже использует 30 секунды длительности каждого доступа:

SELECT recordID, COUNT(*) AS staff_clicks, 30*COUNT(*) AS `estimated duration(seconds)` 
FROM `record_log` 
GROUP BY recordID 

Они оба очень просты, но они ответить на ваш первоначальный вопрос. Есть много вариантов, но трудно узнать, что прописывать без дополнительной информации (временные ограничения, необходимый уровень точности и т. Д.)

+0

Это действительно более простой запрос. К сожалению, он не возвращает указанный результирующий набор. Например, для recordID 33450 он будет возвращать одну строку с продолжительностью «2013-06-20 16: 22: 02' -« 2013-06-20 18: 36: 55 », а не две отдельные длительности, как показано в спецификации. – spencer7593