2014-02-19 2 views
0

У нас есть таблица вроде этого:Как я могу обнаружить последовательные часы в столбце MySQL DATETIME?

 
DESCRIBE time_slots; 

    id   int(11) 
    user_id  int(11) 
    start_time datetime 

Поле start_time всегда час приращение (например 2013-09-04 16:00:00)

Наш ученый данных хочет запросить эту таблицу таким образом, что идентифицирует последовательные start_time записей в user_id такие, что она может создать производную таблицу, которая выглядит следующим образом:

 
    id   int(11) 
    user_id  int(11) 
    start_time datetime 
    end_time datetime 

Например, учитывая эти данные:

 
user_id: 5, start_time: 2013-09-04 16:00:00 
user_id: 5, start_time: 2013-09-04 17:00:00 
user_id: 5, start_time: 2013-09-04 18:00:00 

user_id: 6, start_time: 2013-09-04 16:00:00 
user_id: 6, start_time: 2013-09-04 17:00:00 
user_id: 6, start_time: 2013-09-04 18:00:00 

user_id: 6, start_time: 2013-09-04 20:00:00 
user_id: 6, start_time: 2013-09-04 21:00:00 
user_id: 6, start_time: 2013-09-04 22:00:00 

... мы могли бы получить этот выход:

 
user_id: 5, start_time: 2013-09-04 16:00:00, end_time: 2013-09-04 18:00:00 
user_id: 6, start_time: 2013-09-04 16:00:00, end_time: 2013-09-04 18:00:00 
user_id: 6, start_time: 2013-09-04 20:00:00, end_time: 2013-09-04 22:00:00 

Там может быть кратны этих Начало/конец «блоки» для данного пользователя в день (но они не будут пересекаться).

Любые идеи о том, как это можно сделать в SQL, прежде чем я перейду в План B (настройте денормализованный хранилище данных)?

ответ

0

В зависимости от вашей базы данных ... функции окна могут достичь этого. Создайте столбец, который представляет дельта с предыдущим столбцом (так что вам нужно его упорядочить по user_id, startTime); Затем вы можете использовать этот столбец delta для группировки в группы. Поскольку непрерывные блоки будут представлены «1» в дельте, а новый блок будет иметь большее число.

Возможно, вы также можете достичь этого, выполнив соединение с подзапросом и сориентировавшись на 1, например, соединитесь с ROW_NUMBER и ROW_NUMBER-1, а затем вы можете рассчитать дельта между отметками времени и выполнить некоторую магию с помощью перебора чтобы получить то, что вы хотите. Ключ - это дельта.

Вы можете сделать что-то вроде этого:

SET @prevUser := null; 
SET @prevStartTime := 0; 
SET @groupNumber := 1; 
SET @groupPrevUser := null; 


select 
    user, 
    groupNumber, 
    min(startTime), 
    max(endTime), 
    max(endTime) - min(startTime) as 'duration' 
from 
    (SELECT 
     user, 
      startTime, 
      endTime, 
      delta, 
      IF(delta != 10000 || @groupPrevUser <> user, @groupNumber:[email protected] + 1, @groupNumber) 'groupNumber', 
      @groupPrevUser:=user 
    from 
     (SELECT 
     user, 
      startTime, 
      endTime, 
      IF(@prevUser <> user || @prevStartTime = 0, endTime - startTime, startTime - @prevStartTime) AS delta, 
      @prevUser:=user, 
      @prevStartTime:=startTime 
    FROM 
     queries 
    ORDER BY user , startTime) userData) userGroupData 
group by user , groupNumber 

Получить этот результат:

# user, groupNumber, min(startTime), max(endTime), duration 
bob, 1, 1392060000, 1392080000, 20000 
bob, 2, 1392090000, 1392100000, 10000 
jim, 3, 1392150000, 1392180000, 30000 

Используя эту базовую таблицу:

# user, startTime, endTime 
bob, 1392060000, 1392070000 
bob, 1392070000, 1392080000 
bob, 1392090000, 1392100000 
jim, 1392150000, 1392160000 
jim, 1392160000, 1392170000 
jim, 1392170000, 1392180000 
+2

Функции окон в MySQL не существуют. –

+1

Выполнение самостоятельного соединения с использованием RANK и RANK-1 или user_id, время запуска - 1 час в качестве ключей даст вам ту же способность – mhoglan

+0

btw, InfiniDB, которая является базой данных, совместимой с MySQL (с открытым исходным кодом GPLv2), имеет функции оконного построения, встроенные ontop стандарта MySQL функции. – mhoglan

0

Мое первое предложение заключается в том, что вы изменить схему для добавления счетчика для block_by_id. Тогда ваша проблема - простой min-max. И когда запись будет создана, номер блока можно определить с помощью триггера, увидев, что (1) уже есть запись для этого user_id, и (2) это больше, чем на час раньше, чем новая запись. Я полагаю, вы могли видеть это как денормализованное, и в этом случае идея состоит в том, чтобы выяснить блоки «на лету».

SELECT user_id, MIN(start_time) AS start_time, MAX(start_time) AS start_time 
FROM time_slots t1 
WHERE NOT EXISTS 
    (SELECT 1 FROM time_slots AS t2 WHERE t1.user_id = t2.user_id 
     AND timestampdiff(HOUR, t1.start_time, t2.start_time)=1 
/* replace with date arithmetic function of your RDBMS if need be */ 
    ) 
GROUP BY user_id; 

У меня нет опыта в настройке MySQL. Может быть, разные выражения timediff позволят ему использовать индекс на (user_id, start_time).

Смежные вопросы