2014-01-19 3 views
2

SQL FiddleКак получить записи между датами, которые находятся на разных строках?

У меня есть таблица под названием Event, который регистрирует, когда игроки на входе сервера в многопользовательском и выходе из системы.

+----------------------------------------------+ 
| id | username | type | timestamp   | 
+----------------------------------------------+ 
| 1 | Player3 | login | 2014-01-14 17:00:00 | 
| 2 | Player4 | login | 2014-01-14 17:00:00 | 
| 3 | Player4 | logout | 2014-01-14 17:30:00 | 
| 4 | Player1 | login | 2014-01-14 18:00:00 | 
| 5 | Player2 | login | 2014-01-14 19:00:00 | 
| 6 | Player3 | logout | 2014-01-14 19:00:00 | 
| 7 | Player1 | logout | 2014-01-14 20:00:00 | 
| 8 | Player2 | logout | 2014-01-14 20:00:00 | 
+----------------------------------------------+ 

Я хотел бы получить уникальный список имен пользователей для тех, кто был в сети на определенную метку времени. Так, например, если я хочу знать, кто был в сети по адресу 2014-01-14 18:00:00, он должен вернуть: Player1 и Player3.

То, что я пытался до сих пор:

SELECT * FROM event 
WHERE (timestamp <= '2014-01-14 18:00:00' AND type = 'login') 
AND (timestamp >= '2014-01-14 18:00:00' AND type = 'logout'); 

EDIT2:

Session стол:

+------------------------------------------------------+ 
| id | login_event | logout_event | duration (seconds) | 
+------------------------------------------------------+ 
| 1 | 1   | 6   | xxxxxx    | 
| 2 | 2   | 3   | xxxxxx    | 
| 3 | 4   | 7   | xxxxxx    | 
| 4 | 5   | 8   | xxxxxx    | 
+------------------------------------------------------+ 

ответ

2

Это более сложный, что ваш запрос. Вам нужны люди, чьи самые последние действия в то время входа в систему, по существу:

SELECT username, 
     max(case when type = 'login' and timestamp <= '2014-01-14 18:00:00' 
       then timestamp 
      end) as lastlogin, 
     max(case when type = 'logout' and timestamp <= '2014-01-14 18:00:00' 
       then timestamp 
      end) as lastlogout 
FROM event 
GROUP BY username 
HAVING (lastlogout is null or lastlogout < lastlogin) and lastlogin is not null; 

EDIT:

Кстати, если вы знаете, входа/выхода из записи совершенно точно (никогда не пропускал, никогда не дублируется) , вы также можете сделать это с отсчетом (которые некоторые могут найти легче следить):

SELECT username, 
     sum(type = 'login' and timestamp <= '2014-01-14 18:00:00') as numlogins, 
     sum(type = 'logout' and timestamp <= '2014-01-14 18:00:00') as numlogouts 
FROM event 
GROUP BY username 
HAVING numlogins > numlogouts; 

EDIT (на сессии):

запрос на столе сеанса выглядит проще :

SELECT lie.username 
from session s join 
    event lie 
    on s.login_event = lie.id join 
    event loe 
    on s.logout_event = loe.id 
where lie.timestamp <= '2014-01-14 18:00:00' and 
     loe.timestamp >= '2014-01-14 18:00:00'; 

Это работает и, вероятно, будет лучше на небольших столиках. На больших таблицах я не думаю, что это будет работать так хорошо. Будет работать таблица сеансов, в которой есть две метки времени, с индексом на них.

+0

Спасибо! Я хочу, чтобы записи ввода/выхода были точными. Но какой из них лучше работает с большим количеством записей в таблице? – Jonathan

+0

Производительность должна быть одинаковой для этих двух запросов. Вы можете повысить производительность, указав индекс на timestamp и добавив предложение where к первой версии, например 'where timestamp между '2014-01-13' и '2014-01-14 18: 00: 00'' (предполагая, что люди не проводят больше 42 часов). Это значительно сократит объем данных. –

+0

Можете ли вы взглянуть на мое редактирование? У меня есть другая таблица под названием «Сессия», было бы лучше запросить эту таблицу? – Jonathan

0
SELECT username FROM 
(SELECT username,timestamp as intime, 
     (SELECT min(timestamp) 
     FROM event e2 
     WHERE e2.username = e1.username 
      AND e2.timestamp > e1.timestamp 
     AND e2.type = 'logout') as outtime 
FROM event e1 
WHERE e1.type = 'login' 
)t1 
WHERE '2014-01-14 18:00:00' BETWEEN t1.intime AND t1.outtime; 

Вы можете сопоставить временную метку как intime и outtime, а затем посмотреть, установлено ли ваше указанное время между этими временами. sqlFiddle

или вы можете использовать HAVING, чтобы вынуть 1 уровень подзапроса.

SELECT username,timestamp as intime, 
(SELECT min(timestamp) 
FROM event e2 
WHERE e2.username = e1.username 
AND e2.timestamp > e1.timestamp 
AND e2.type = 'logout') as outtime 
FROM event e1 
WHERE e1.type = 'login' 
HAVING '2014-01-14 18:00:00' BETWEEN intime AND outtime; 
Смежные вопросы