Domanda

Buona giornata a tutti.

Ho questo ambiente: su un DB MySQL, ogni volta che un utente accede su un sito, viene creata una nuova riga, con il suo nome e il tempo in cui accede. Poiché il sistema è reciproco esclusivo, ci sarà solo un utente in un determinato momento e, se arriverà un nuovo utente, quello registrato viene disconnesso.

Ora mi hanno chiesto di calcolare il tempo totale di tutti gli utenti sul sistema, quindi in sostanza, devo riassumere tutte le differenze di tempo da un accesso e il prossimo.

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

Vorrei chiedere la tua opinione, dal momento che ci sono molti accessi, che è il modo migliore per calcolare il tempo totale registrato di un utente? In questo esempio "gamma" avrà un tempo di accesso di 56 secondi e l'ultimo accesso di beta potrebbe essere ignorato, poiché sarà online al momento dell'esecuzione di questo controllo. Quindi "beta" avrà una sola voce.

C'è un modo in cui lo calcola tramite query? O è meglio aggiungere una colonna "tempo online" e lasciare che il Sistem calcola ogni volta che un discorso dell'utente quanto tempo ha speso online?

È stato utile?

Soluzione

Ciò richiede un auto-goin se vuoi farlo in MySQL. È un dolore al collo fare un auto-koin perché MySQL non ha una funzione rowum incorporata. Ma è ancora fattibile.

Innanzitutto, dobbiamo creare una sottoquery per creare una tabella virtuale che simula SELECT rownum, user, timestamp FROM login che possiamo fare così. 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

Successivamente, dobbiamo fare un auto-koin di questa tabella virtuale per una copia di se stessa. Ciò che vogliamo in questo set di risultati è un elenco di tutte le coppie consecutive di righe nella tabella. Quella domanda è una palla di pelo - mette il strutturato in Structured Query Language. Ma funziona. Ecco qui: 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

L'intero trucco per confrontare le righe consecutive è il

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

Modello di query. Rownum+1 = Rownum Thing raccoglie le righe con numeri di riga consecutivi insieme.

Successivamente, dobbiamo riassumere il risultato di quella query per ottenere il tempo totale dell'accesso per ciascun utente. Funzionerà in questo modo:

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

Sembra questo: http://sqlfiddle.com/#!2/bf6ef/5/0

Questa è l'intera query messa insieme.

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

Non è reale intuitivo per qualcuno abituato a pensiero procedurale, algoritmico. Ma questo è il modo in cui fai questo tipo di confronto di fila consecutiva in SQL.

Altri suggerimenti

Prova con questo ... meno o più è la soluzione del tuo 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

:-) Ci vediamo lunedì !!!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top