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

SQL - Trova tutti i tempi di inattività e le lunghezze dei tempi di inattività dai dati MySQL (set di righe con timestamp e messaggi di stato)

Ecco un approccio.

Inizia ordinando le righe di stato in base al timestamp (vista in linea alias s ). Quindi usa le variabili utente MySQL per mantenere i valori delle righe precedenti, mentre elabori ogni riga.

Quello che stiamo veramente cercando è uno stato "su" che segua immediatamente una sequenza di stato "giù". E quando troviamo quella riga con lo stato "su", ciò di cui abbiamo veramente bisogno è il primo timestamp della serie precedente di stato "giù".

Quindi, qualcosa del genere funzionerà:

SELECT d.start_down
     , d.ended_down
  FROM (SELECT @i := @i + 1 AS i
             , @start := IF(s.status = 'down' AND (@status = 'up' OR @i = 1), s.time, @start) AS start_down
             , @ended := IF(s.status = 'up' AND @status = 'down', s.time, NULL) AS ended_down
             , @status := s.status
         FROM (SELECT t.time
                    , t.status
                 FROM mydata t
                WHERE t.status IN ('up','down')
                ORDER BY t.time ASC, t.status ASC
              ) s
         JOIN (SELECT @i := 0, @status := 'up', @ended := NULL, @start := NULL) i
      ) d
WHERE d.start_down IS NOT NULL
  AND d.ended_down IS NOT NULL

Funziona per il particolare set di dati che mostri.

Ciò che questo non gestisce (ciò che non restituisce) è un periodo "down" che non è ancora terminato, ovvero una sequenza di stato "down" senza uno stato "up" successivo.

Per evitare un'operazione di ordinamento dei file per restituire le righe in ordine, ti consigliamo un indice di copertura su (time,status) . Questa query genererà una tabella temporanea (MyISAM) per materializzare la vista in linea alias come d .

NOTA: Per capire cosa sta facendo questa query, rimuovi quella query più esterna ed esegui solo la query per la vista in linea alias come d (puoi aggiungere s.time all'elenco di selezione.)

Questa query ottiene ogni riga con uno stato "su" o "giù". Il "trucco" è che sta assegnando sia un tempo di "inizio" che di "fine" (contrassegnando un periodo negativo) solo alle righe che terminano un periodo "basso". (Cioè, la prima riga con uno stato "su" che segue le righe con uno stato "giù".) È qui che viene svolto il vero lavoro, la query più esterna filtra semplicemente tutte le righe "extra" in questo set di risultati (che noi non è necessario.)

SELECT @i := @i + 1 AS i
     , @start := IF(s.status = 'down' AND (@status = 'up' OR @i = 1), s.time, @start) AS start_down
     , @ended := IF(s.status = 'up' AND @status = 'down', s.time, NULL) AS ended_down
     , @status := s.status
     , s.time
  FROM (SELECT t.time
             , t.status
          FROM mydata t
         WHERE t.status IN ('up','down')
         ORDER BY t.time ASC, t.status ASC
       ) s
  JOIN (SELECT @i := 0, @status := 'up', @ended := NULL, @start := NULL) i

Lo scopo della visualizzazione in linea alias come s è ottenere le righe ordinate in base al valore del timestamp, in modo da poterle elaborare in sequenza. La vista in linea alias come i è solo lì così possiamo inizializzare alcune variabili utente all'inizio della query.

Se fossimo in esecuzione su Oracle o SQL Server, potremmo utilizzare "funzioni analitiche" o "funzioni di classificazione" (come vengono denominate, rispettivamente). MySQL non fornisce nulla del genere, quindi dobbiamo "rotolare le nostre ".