Mysql
 sql >> Base de données >  >> RDS >> Mysql

SQL - Trouver tous les temps d'arrêt et les durées des temps d'arrêt à partir des données MySQL (ensemble de lignes avec horodatage et messages d'état)

Voici une approche.

Commencez par obtenir les lignes d'état dans l'ordre par horodatage (vue en ligne aliasée en tant que s ). Utilisez ensuite les variables utilisateur MySQL pour conserver les valeurs des lignes précédentes, au fur et à mesure que vous parcourez chaque ligne.

Ce que nous recherchons vraiment, c'est un statut "up" qui suit immédiatement une séquence de statuts "down". Et lorsque nous trouvons cette ligne avec le statut "up", ce dont nous avons vraiment besoin est le premier horodatage de la série précédente de statut "down".

Donc, quelque chose comme ça fonctionnera :

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

Cela fonctionne pour l'ensemble de données particulier que vous affichez.

Ce que cela ne gère pas (ce qu'il ne renvoie pas), c'est une période 'down' qui n'est pas encore terminée, c'est-à-dire une séquence de statut 'down' sans statut 'up' suivant.

Pour éviter qu'une opération de tri de fichiers renvoie les lignes dans l'ordre, vous aurez besoin d'un index de couverture sur (time,status) . Cette requête va générer une table temporaire (MyISAM) pour matérialiser la vue en ligne alias d .

REMARQUE : Pour comprendre ce que fait cette requête, décollez cette requête la plus externe et exécutez uniquement la requête pour la vue en ligne alias d (vous pouvez ajouter s.time à la liste de sélection.)

Cette requête obtient chaque ligne avec un statut 'up' ou 'down'. Le "truc" est qu'il attribue à la fois une heure de "début" et de "fin" (marquant une période d'arrêt) uniquement aux lignes qui terminent une période "d'arrêt". (C'est-à-dire, la première ligne avec un statut "up" suivant les lignes avec un statut "down".) C'est là que le vrai travail est fait, la requête la plus externe filtre simplement toutes les lignes "supplémentaires" dans ce jeu de résultats (que nous pas besoin.)

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

Le but de la vue en ligne aliasée comme s est d'obtenir les lignes triées par valeur d'horodatage, afin que nous puissions les traiter en séquence. La vue en ligne alias i est juste là pour que nous puissions initialiser certaines variables utilisateur au début de la requête.

Si nous utilisions Oracle ou SQL Server, nous pourrions utiliser des "fonctions analytiques" ou des "fonctions de classement" (comme elles sont nommées, respectivement.) MySQL ne fournit rien de tel, nous devons donc "rouler nos propres ".