Pregunta

Buen día a todos.

Tengo este entorno: en un MySQL DB, cada vez que un usuario inicia sesión en un sitio, se crea una nueva fila, con su nombre y el momento en que inicia sesión. Dado que el sistema es mutuo exclusivo, solo habrá un usuario en un momento determinado, y si llega un nuevo usuario, el que se registra se registra.

Ahora me han pedido que calcule el tiempo total de todos los usuarios en el sistema, por lo que básicamente tengo que unir todas las diferencias de tiempo de un inicio de sesión y el 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

Me gustaría pedirle su opinión, ya que hay muchos inicios de sesión, ¿cuál es la mejor manera de calcular el tiempo total registrado de un usuario? En este ejemplo, "Gamma" tendrá un tiempo de inicio de sesión de 56 segundos, y el último inicio de sesión de Beta podría ignorarse, ya que estará en línea en el momento de la ejecución de este cheque. Entonces, "beta" solo tendrá una entrada.

¿Hay alguna forma de calcularlo a través de la consulta? ¿O es mejor agregar una columna "tiempo en línea" y dejar que el SISTEM calcule cada vez que el inicio de sesión de un usuario ha pasado el tiempo que ha pasado en línea?

¿Fue útil?

Solución

Esto requiere un auto unirse si quieres hacerlo en MySQL. Es un dolor en el cuello hacer un un unión porque MySQL no tiene una función de Rownum incorporada. Pero todavía es factible.

Primero, necesitamos crear una subconsulta para crear una tabla virtual simulando SELECT rownum, user, timestamp FROM login que podemos hacer así. 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

A continuación, necesitamos hacer una unión de esta tabla virtual a una copia de sí misma. Lo que queremos en este conjunto de resultados es una lista de todos los pares consecutivos de filas en la tabla. Esa consulta es una bola de pelo, pone la estructurado en lenguaje de consulta estructurado. Pero funciona. Aquí 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 el truco para comparar filas consecutivas es la

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

Patrón de consulta. La cosa de Rownum+1 = Rownum reúne filas con números de fila consecutivos juntas.

A continuación, debemos resumir el resultado de esa consulta para que el tiempo total inicie sesión para cada usuario. Eso funcionará así:

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

Eso se ve así: http://sqlfiddle.com/#!2/bf6ef/5/0

Esta es toda la consulta junta.

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

No es realmente intuitivo para alguien acostumbrado al pensamiento de procedimiento, algorítmico,. Pero esta es la forma en que haces este tipo de comparación de filas consecutivas en SQL.

Otros consejos

Intente con esto ... menos o más es la solución de su 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

:-) ¡Nos vemos el lunes !!!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top