Mysql
 sql >> Database >  >> RDS >> Mysql

Metodo per trovare le lacune nei dati delle serie temporali in MySQL?

Per cominciare, riassumiamo il numero di voci per ora nella tabella.

SELECT CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) hour,
       COUNT(*) samplecount
  FROM table
 GROUP BY CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME)

Ora, se registri qualcosa ogni sei minuti (dieci volte all'ora), tutti i valori del conteggio dei campioni dovrebbero essere dieci. Questa espressione:CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) sembra peloso ma semplicemente tronca i tuoi timestamp all'ora in cui si verificano azzerando minuti e secondi.

Questo è ragionevolmente efficiente e ti consentirà di iniziare. È molto efficiente se puoi inserire un indice nella colonna entry_time e limitare la tua query, diciamo, agli esempi di ieri, come mostrato qui.

SELECT CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) hour,
       COUNT(*) samplecount
  FROM table
 WHERE entry_time >= CURRENT_DATE - INTERVAL 1 DAY
   AND entry_time < CURRENT_DATE
 GROUP BY CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME)

Ma non è molto bravo a rilevare intere ore che passano con campioni mancanti. È anche un po' sensibile al jitter nel campionamento. Cioè, se il tuo campione della parte superiore dell'ora è a volte in anticipo di mezzo secondo (10:59:30) e talvolta in ritardo di mezzo secondo (11:00:30), il conteggio del riepilogo orario sarà storto. Quindi, questa cosa del riepilogo dell'ora (o riepilogo del giorno, o riepilogo dei minuti, ecc.) Non è a prova di proiettile.

Hai bisogno di una query di auto-unione per ottenere le cose perfettamente giuste; è un po' più una palla di pelo e non altrettanto efficiente.

Iniziamo creando noi stessi una tabella virtuale (subquery) come questa con campioni numerati. (Questo è un problema in MySQL; alcuni altri DBMS costosi lo rendono più semplice. Non importa.)

  SELECT @sample:[email protected]+1 AS entry_num, c.entry_time, c.value
    FROM (
        SELECT entry_time, value
      FROM table
         ORDER BY entry_time
    ) C,
    (SELECT @sample:=0) s

Questa piccola tabella virtuale fornisce entry_num, entry_time, valore.

Il prossimo passo, lo uniamo a se stesso.

SELECT one.entry_num, one.entry_time, one.value, 
       TIMEDIFF(two.value, one.value) interval
  FROM (
     /* virtual table */
  ) ONE
  JOIN (
     /* same virtual table */
  ) TWO ON (TWO.entry_num - 1 = ONE.entry_num)

Questo allinea i tavoli uno accanto all'altro compensati da un'unica voce, regolata dalla clausola ON del JOIN.

Infine scegliamo i valori da questa tabella con un interval maggiore della tua soglia e ci sono i tempi dei campioni subito prima di quelli mancanti.

La query di unione automatica generale è questa. Te l'avevo detto che era una palla di pelo.

SELECT one.entry_num, one.entry_time, one.value, 
       TIMEDIFF(two.value, one.value) interval
  FROM (
    SELECT @sample:[email protected]+1 AS entry_num, c.entry_time, c.value
      FROM (
          SELECT entry_time, value
            FROM table
           ORDER BY entry_time
      ) C,
      (SELECT @sample:=0) s
  ) ONE
  JOIN (
    SELECT @sample2:[email protected]+1 AS entry_num, c.entry_time, c.value
      FROM (
          SELECT entry_time, value
            FROM table
           ORDER BY entry_time
      ) C,
      (SELECT @sample2:=0) s
  ) TWO ON (TWO.entry_num - 1 = ONE.entry_num)

Se devi farlo in produzione su una tabella di grandi dimensioni, potresti volerlo fare per un sottoinsieme dei tuoi dati. Ad esempio, potresti farlo ogni giorno per i campioni dei due giorni precedenti. Questo sarebbe abbastanza efficiente e ti assicurerebbe anche di non trascurare alcun campione mancante a mezzanotte. Per fare questo, i tuoi tavolini virtuali numerati di righe assomiglierebbero a questo.

  SELECT @sample:[email protected]+1 AS entry_num, c.entry_time, c.value
    FROM (
        SELECT entry_time, value
      FROM table
         ORDER BY entry_time
         WHERE entry_time >= CURRENT_DATE - INTERVAL 2 DAY
           AND entry_time < CURRENT_DATE /*yesterday but not today*/
    ) C,
    (SELECT @sample:=0) s