Pergunta

Bom dia a todos.

Eu tenho esse ambiente: em um db mysql, toda vez que um usuário faz login em um site, ele é criado uma nova linha, com seu nome e o tempo em que ele login. Como o sistema é exclusivo mútuo, haverá apenas um usuário em um determinado momento e, se um novo usuário chegar, o registrado será regoziado.

Agora eles me pediram para calcular o tempo total de todos os usuários do sistema, então, basicamente, tenho que somar juntos o tempo todo diferenças de um login e o próximo.

user  |       timestamp     |
------------------------------
alpha | 2013-01-19 03:14:07
beta  | 2013-01-20 11:24:04
alpha | 2013-01-21 02:11:37
alpha | 2013-01-21 03:10:31    <---- a user could login twice, it is normal
gamma | 2013-01-21 11:24:04
beta  | 2013-01-21 11:25:00

Gostaria de perguntar sua opinião, já que existem muitos logins, que é a melhor maneira de calcular o tempo total de log de um usuário? Neste exemplo, "Gamma" terá um tempo de login de 56 segundos, e o último login de beta poderá ser ignorado, pois estará online no momento da execução desta verificação. Portanto, "beta" terá apenas uma entrada.

Existe uma maneira de calcular isso via consulta? Ou é melhor adicionar uma coluna "Time Online" e deixar o Sistem calcular cada vez que um logout do usuário, quanto tempo gasta online?

Foi útil?

Solução

Isso requer uma auto-joia se você quiser fazê-lo no MySQL. É uma dor no pescoço fazer uma auto-joia porque o MySQL não tem função de Rownum. Mas ainda é factível.

Primeiro, precisamos criar uma subconeração para criar uma tabela virtual simulando SELECT rownum, user, timestamp FROM login o que podemos fazer assim. http://sqlfiddle.com/#!2/bf6ef/2/0

SELECT @a:=@a+1 AS rownum, user, timestamp
    FROM (
        SELECT user, timestamp
          FROM login
         ORDER BY timestamp
    ) C,
    (SELECT @a:=0) s

Em seguida, precisamos fazer uma auto-joia desta tabela virtual para uma cópia de si mesma. O que queremos neste conjunto de resultados é uma lista de todos os pares consecutivos de linhas da tabela. Essa consulta é uma bola de cabelo - coloca o estruturada dentro linguagem de consulta estruturada. Mas funciona. Aqui está: http://sqlfiddle.com/#!2/bf6ef/4/0

SELECT first.user AS fuser, 
       first.timestamp AS ftimestamp,
       second.user AS suser,
       second.timestamp as stimestamp,
       TIMESTAMPDIFF(SECOND, first.timestamp, second.timestamp) AS timeloggedin

  FROM (
       SELECT @a:=@a+1 AS rownum, user, timestamp
         FROM (
             SELECT user, timestamp
               FROM login
           ORDER BY timestamp
              ) C,
          (SELECT @a:=0) s
        ) AS first
  JOIN (
       SELECT @b:=@b+1 AS rownum, user, timestamp
         FROM (
             SELECT user, timestamp
               FROM login
           ORDER BY timestamp
              ) C,
          (SELECT @b:=0) s
        ) AS second ON first.rownum+1 = second.rownum

Todo o truque para comparar linhas consecutivas é o

SELECT (virtual_table) AS first
  JOIN (virtual_table) AS second ON first.rownum+1 = second.rownum

padrão de consulta. A coisa de Rownum+1 = Rownum reúne linhas com números consecutivos de linha juntos.

Em seguida, precisamos resumir o resultado dessa consulta para obter o tempo total conectado a cada usuário. Isso vai funcionar assim:

  SELECT user, SUM(timeloggedin) AS timeloggedin
    FROM (
          /* the self-joined query */
         ) AS selfjoin
   GROUP BY user
   ORDER BY user

Isso se parece com o seguinte: http://sqlfiddle.com/#!2/bf6ef/5/0

Esta é toda a consulta montada.

SELECT user, SUM(timeloggedin) AS timeloggedin
  FROM (
      SELECT first.user AS user, 
             TIMESTAMPDIFF(SECOND, first.timestamp, second.timestamp) AS timeloggedin
        FROM (
             SELECT @a:=@a+1 AS rownum, user, timestamp
         FROM (
                   SELECT user, timestamp
                     FROM login
                 ORDER BY timestamp
                    ) C,
                (SELECT @a:=0) s
              ) AS first
        JOIN (
             SELECT @b:=@b+1 AS rownum, user, timestamp
               FROM (
                   SELECT user, timestamp
                     FROM login
                 ORDER BY timestamp
                    ) C,
                (SELECT @b:=0) s
              ) AS second ON first.rownum+1 = second.rownum
         ) AS selfjoin
   GROUP BY user
   ORDER BY user

Não é realmente intuitivo para alguém acostumado a procedimento, algorítmico e pensando. Mas é assim que você faz esse tipo de comparação consecutiva na linha no SQL.

Outras dicas

Tente com isso ... menos ou mais é a solução do seu problema ...

    CREATE TABLE `matteo` (
      `user` varchar(20) DEFAULT NULL,
      `timestamp` int(11) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    INSERT INTO matteo(user, `timestamp`) VALUES ('alpha', 7);
    INSERT INTO matteo(user, `timestamp`) VALUES ('beta', 9);
    INSERT INTO matteo(user, `timestamp`) VALUES ('alpha', 17);
    INSERT INTO matteo(user, `timestamp`) VALUES ('alpha', 27);
    INSERT INTO matteo(user, `timestamp`) VALUES ('gamma', 77);
    INSERT INTO matteo(user, `timestamp`) VALUES ('beta', 97);

    select a.*,b.*,b.`timestamp`-a.`timestamp` as delta
    from
    (SELECT @rownum := @rownum + 1 AS id,t.*
          FROM matteo t,(SELECT @rownum := 0) r) a
    join
    (SELECT @rownum2 := @rownum2 + 1 AS id,t.*
          FROM matteo t,(SELECT @rownum2 := 0) r) b 
    where a.id=b.id-1

:-) veja você segunda-feira !!!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top