2014-10-10 5 views
2

Название, вероятно, оставляет некоторые вопросы, поэтому я подробно объясню.SQL Сбор заявок на основе интервала 30 минут

У меня есть таблица (MySQL), содержащая сообщения чата, Эти сообщения содержат все столбцы datetime даты, когда они добавлены.

Теперь, когда я хочу достичь, я получаю количество сеансов, которые были у вас.

Теперь, что такое сеанс? Новый сеанс начинается с 30 минут или более с момента предыдущего сообщения.

Так, например, с данными:

2014-01-01 00:00:01 
2014-01-01 00:20:01 
2014-01-01 00:40:01 
2014-01-01 00:60:01 

будет один сеанс

2014-01-01 00:00:01 <-- 
2014-01-01 00:32:01 <-- 
2014-01-01 00:35:01 
2014-01-01 01:00:01 
2014-01-01 02:00:01 <-- 
2014-01-01 02:20:01 

Было бы три сессии, начиная заново, когда я положил стрелку на старте.

Мне не обязательно нужен пример DQL. MySQL будет в порядке, я надеюсь, что кто-то может мне помочь.

Редактировать: Ответ, приведенный ниже, кажется, работает на скрипке, но не на наших серверах MySQL, работающих под управлением 5.5.4. Удовлетворение, если это определенная настройка, или что sqlFiddle просто не работает должным образом.

+0

30 минут с того времени? Начало каждого часа? Некоторое случайное сообщение? Что произойдет, если у вас есть сообщения каждые 5 минут в течение 3 часов? Вы считаете, что вперед вовремя (как вы выбираете стартовые точки) или назад от текущего (что может изменить ваши сеансы)? Это некоторая форма анализа пробелов, которую MySQL не очень хорошо подходит (большинство других СУБД имеют функции, которые дадут дополнительную поддержку для таких вещей). –

+0

Я надеялся, что мой пример будет иметь смысл. Так что да, оно начиналось с сообщения и заканчивалось последним сообщением, которое попадает в диапазон 30 минут последнего сообщения. К сожалению, я не могу отойти от MySQL, я привязан к MySQL и PHP –

+0

И сообщение каждые 5 минут в течение трех часов приведет к 3-часовому сеансу. –

ответ

0

Для моего конкретного случая я решил его с помощью запроса, как это:

SELECT * 
FROM chat c 
WHERE NOT EXISTS 
     (
     SELECT * 
     FROM chat c2 
     WHERE c2.date_add <= (c.date_add + INTERVAL 30 MINUTE) 
     AND  c2.date_add > c.date_add 
     ) 

Единственная проблема здесь в том, что я не могу видеть, когда сессия началась, но это достаточно функциональность для моего конкретного случая. Я с радостью согласимся на лучший ответ!

+0

Ваше решение отображает сообщение LAST из сеанса. Мое решение показывает сообщение FIRST в сеансе, которое я понимаю, что вы предпочитаете. Все, что я сделал, это инвертировать ваше окно времени. – Eric

1

MySQL, к сожалению, не имеет так называемых оконных функций (что делает большинство других основных РСУБД), поэтому нам нужно приманить один из наших собственных. На самом деле это не так сложно, но было бы неплохо иметь поддержку ...

В любом случае, я сказал, что нам нужно было приманить функцию LAG(), но это действительно так, что мы можем сравнить с увеличением группирования счетчика, так что мы можем на самом деле вырезать шаг, Сорт:

SELECT sentAt, 
     @Session := IF(sentAt < @SessionBoundary, @Session, @Session + 1) AS session, 
     @SessionBoundary := ADDTIME(sentAt, '00:30:00') AS sessionBoundary 
FROM Message 
JOIN (SELECT @Session := 0) n 
ORDER BY sentAt 

SQL Fiddle Example

Критически, обратите внимание, что, как это хорошая практика, чтобы использовать эксклюзивны верхнюю границу "(<) для положительные типы непрерывного диапазона (например, date/time/timestamps), это 30th минута, что на самом деле начинается ваша новая сессия. То есть, начальное сообщение в 13:00 означает, что следующий сеанс начинается в 13:30 (без дополнительных сообщений). У этого есть приятное свойство сделать все, что выстраивается красиво, и что мне не нужно беспокоиться о странном поведении с дробными секундами, которые я, возможно, не указал.
В любом случае, это возвращает результаты так:

sentAt    session sessionBoundary 
2014-01-01 00:00:01 1  2014-01-01 00:30:01 
2014-01-01 00:32:01 2  2014-01-01 01:02:01 
2014-01-01 00:35:01 2  2014-01-01 01:05:01 
2014-01-01 01:00:01 2  2014-01-01 01:30:01 
2014-01-01 02:00:01 3  2014-01-01 02:30:01 
2014-01-01 02:20:01 3  2014-01-01 02:50:01 

Теперь, так как все, что вам нужен был простой подсчет, сколько session s были, вы можете обернуть его в качестве подзапроса:

SELECT MAX(session) 
FROM (SELECT sentAt, 
      @Session := IF(sentAt < @SessionBoundary, @Session, @Session + 1) AS session, 
      @SessionBoundary := ADDTIME(sentAt, '00:30:00') AS sessionBoundary 
     FROM Message 
     JOIN (SELECT @Session := 0) n 
     ORDER BY sentAt) MessageSession 


(Примечание:. по какой-то причине я не понимаю, используя первоначальную работу в качестве подзапроса является причиной скрипку, чтобы начать на 0 вместо 1, что и до Пожалуйста, проверьте это на вашем сервере,s вам может понадобиться инициализировать с помощью @Session = 1 вместо 0 или использовать что-то вроде COUNT(DISTINCT session)).

... И все готово.


Хотя вы только перечисленные желая граф, как только вы сеанс группировки вы можете иметь все виды весело с вашими данными. Теперь тривиально получить MAX(sentAt)/MIN(sentAt) для каждой группы, количество сообщений в группе или что-то еще. Например, вы могли бы сказать, «найти все давно запущенные сеансы» через что-то вроде этого:

SELECT session, 
     MIN(sentAt) AS firstMessageAt, MAX(sentAt) AS lastMessageAt, COUNT(*) AS messages 
FROM (SELECT sentAt, 
      @Session := IF(sentAt < @SessionBoundary, @Session, @Session + 1) AS session, 
      @SessionBoundary := ADDTIME(sentAt, '00:30:00') AS sessionBoundary 
     FROM Message 
     JOIN (SELECT @Session := 0) n 
     ORDER BY sentAt) MessageSession 
GROUP BY session 
HAVING ADDTIME(MIN(sentAt), '24:00:00') < MAX(sentAt) 

(найти всю сессию, которая уже в течение не менее 24 часов)

+0

Я сохраню это в своей памяти, пытаясь преобразовать свой запрос в DQL сейчас, возможно, проще, чем использовать ваше решение. Кажется, лучше. –

+0

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

+0

Ах да, похоже, пока не выясняется, почему, но ЕСЛИ никогда не ошибается. –

0

Твердая частью является то, что SQL является не процедурный язык, когда проблема является процедурной.

Ваше предложение - разумный запрос, который может быть выполнен в чистом SQL. Если вы хотите, чтобы идти дальше, я бы посоветовал вам использовать процедурный сценарий, который может быть записан на многих языках, Python, Perl, Ruby, ...

В языке псевдо, это может быть:

long last=-1800 // time of previous line 
long beg = -1 // begin time for current session 
CREATE_QUERY_FOR : SELECT UNIX_TIMESTAMP(dat) FROM chat ORDER BY dat 
LOOP_PER_LINE_FETCHED getting dat 

if (dat - last > 30 * 60) 
then 
    if last != -1 // we had a session 
    then NOTE SESSION begin at beg and ending at last 
    endif 

    beg = dat // start a new session 
endif 

last = dat 

END_LOOP 

ИМХО это единственный способ (или, по крайней мере, самый простой и эффективный), чтобы получить все сеансы с начала и конца - но я должен признать, это не может быть то, что вы просите ...

+0

Да, я действительно не чувствую, что собираю несколько записей 10 тыс. Записей и начинаю перебирать их. Часовая скрипка работает отлично, но по какой-то причине она не работает на моей тестовой среде. –

+0

@MathijsSegers: вам не нужно извлекать все записи перед обработкой. Все цитируемые языки сценариев позволяют обрабатывать записи во время их выборки или, точнее, для забора записей по одному или по партиям определенного размера. –

0
SELECT distinct * 
FROM chat c 
WHERE NOT EXISTS 
     (
     SELECT * 
     FROM chat c2 
     WHERE c2.date_add > (c.date_add - INTERVAL 30 MINUTE) 
     AND  c2.date_add < c.date_add 
     ) 
+0

Нет. Мне нужны оба, и, кроме того, этот запрос слишком медленный, занимает несколько минут в моем тесте, поэтому он не работает. –

+0

Если вы хотите, то и просто объедините свой запрос с моим. И я не вижу, как характеристики производительности этого запроса могут существенно отличаться от ваших. Они те же, но с инвертированной логикой. – Eric

+0

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

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

  • Нет связанных вопросов^_^