Соответствие действиям пользователя из таблицы MySQL: каждое ЗАКРЫТО должно соответствовать предыдущему ОТКРЫТОМУ для данного пользователя - PullRequest
2 голосов
/ 11 января 2012

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

TABLE USER_ACTIONS
------------------------
USER | TYPE  | TIMESTAMP
------------------------
a    | OPEN  | 0
b    | OPEN  | 1
a    | CLOSE | 2
a    | OPEN  | 3
b    | CLOSE | 4
a    | CLOSE | 4
a    | OPEN  | 5  <-- "orphaned" OPEN, with no corresponding CLOSE. Should be ignored.
c    | OPEN  | 3
c    | CLOSE | 5
a    | OPEN  | 6
a    | CLOSE | 8

Я хотел бы получить список времени транзакций из этого.Каждое ЗАКРЫТИЕ должно соответствовать предыдущему ОТКРЫТОМУ для конкретного пользователя.

Результаты, которые я хотел бы получить, будут выглядеть примерно так:

USER | TRANSACTION_TIME
-----------------------
a    | 2
b    | 3
a    | 1
c    | 2
a    | 2

Меня не волнует порядок.

Я знаю, что это можно сделать программно, но возможно ли это сделать с каким-нибудь умным SQL?

ОБНОВЛЕНИЕ:

Чтобы сделать это программнообщая идея заключается в том, чтобы ...

  1. Выбрать все действия "ЗАКРЫТЬ", упорядочив по убыванию TIMESTAMP.
  2. Для каждого из тех, кто в этом списке, попробуйте найтипредыдущее действие «OPEN», выполненное тем же пользователем.Ограничьте значение TIMESTAMP до действия «ЗАКРЫТЬ» TIMESTAMP, сортируйте результаты по TIMESTAMP DESC и ограничьте их значением 1.
  3. Для этой пары рассчитайте разницу во времени и выведите результат.

Вот некоторый псевдокод, но на самом деле я хотел бы, чтобы SQL делал это умно:

for each CLOSE_ACTION IN ("SELECT USER, TYPE, TIMESTAMP FROM USER_ACTIONS WHERE TYPE='CLOSE' ORDER BY TIMESTAMP DESC;") {
    OPEN_ACTION = "SELECT USER, TYPE, TIMESTAMP FROM USER_ACTIONS
                   WHERE TYPE='OPEN'
                   AND USER='<CLOSE_ACTION.USER>'
                   AND TIMESTAMP='<CLOSE_ACTION.TIMESTAMP>'
                   ORDER BY TIMESTAMP DESC
                   LIMIT 1";
    if OPEN_ACTION != empty/null then {
        print CLOSE_ACTION.USER, CLOSE_ACTION.TIMESTAMP - OPEN_ACTION.TIMESTAMP;
    }
}

Ответы [ 3 ]

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

Принимает каждое событие ЗАКРЫТЬ и сопоставляет его с предшествующим событием, если и только если предыдущее событие ОТКРЫТО.

SELECT
  OPEN.user,
  OPEN.transaction_time
  CLOSE.transaction_time
FROM
  user_actions as CLOSE
INNER JOIN
  user_actions as OPEN
    ON  OPEN.user = CLOSE.user
    AND OPEN.transaction_time = (SELECT MAX(transaction_time) FROM user_action
                                 WHERE user = CLOSE.user
                                 AND transaction_time < CLOSE.transaction_time
                                 AND type='OPEN')
WHERE
    CLOSE.type = 'CLOSE'
0 голосов
/ 12 января 2012

Приведенный ниже запрос отлично работает для поставленного вопроса. Я выполнил его на своей локальной машине, и, кажется, он работает нормально.

select u1.user_name ,u2.timestamp- max(u1.timestamp) difference
from user_actions u1,user_actions u2 
where u1.type = 'OPEN' and 
u2.type = 'CLOSE' and
u1.timestamp <u2.timestamp and u1.user_name = u2.user_name
group by (u1.user_name , u2.timestamp);
0 голосов
/ 11 января 2012

Попробуйте:

select user,
       timestamp,
       (select min(timestamp) 
        from user_actions u 
        where a.user = u.user and 
              u.type = 'CLOSE' and 
              u.timestamp > a.timestamp) - timestamp
from user_actions a
where type = 'OPEN'

(Предполагается, что всегда будет подходящее закрытие для каждого открытия.)

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