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

Méthode pour trouver des lacunes dans les données de séries chronologiques dans MySQL ?

Pour commencer, résumons le nombre d'entrées par heure dans votre tableau.

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)

Maintenant, si vous enregistrez quelque chose toutes les six minutes (dix fois par heure), toutes vos valeurs de nombre d'échantillons doivent être de dix. Cette expression :CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) semble poilu mais il tronque simplement vos horodatages à l'heure à laquelle ils se produisent en mettant à zéro la minute et la seconde.

Ceci est raisonnablement efficace et vous aidera à démarrer. C'est très efficace si vous pouvez mettre un index sur votre colonne entry_time et limiter votre requête, disons, aux échantillons d'hier, comme indiqué ici.

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)

Mais il n'est pas très efficace pour détecter des heures entières qui passent avec des échantillons manquants. C'est aussi un peu sensible à la gigue dans votre échantillonnage. C'est-à-dire que si votre échantillon du début de l'heure est parfois une demi-seconde en avance (10:59:30) et parfois une demi-seconde en retard (11:00:30), vos décomptes horaires seront décalés. Donc, ce truc de résumé d'heure (ou de résumé de jour, ou de résumé de minute, etc.) n'est pas à l'épreuve des balles.

Vous avez besoin d'une requête d'auto-jointure pour obtenir des informations parfaitement correctes ; c'est un peu plus une boule de poils et pas aussi efficace.

Commençons par créer nous-mêmes une table virtuelle (sous-requête) comme celle-ci avec des échantillons numérotés. (C'est pénible avec MySQL ; d'autres SGBD coûteux facilitent les choses. Peu importe.)

  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

Cette petite table virtuelle donne entry_num, entry_time, value.

Prochaine étape, nous le joignons à lui-même.

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)

Cela aligne les tables les unes à côté des autres décalées par une seule entrée, régie par la clause ON du JOIN.

Enfin, nous choisissons les valeurs de cette table avec un interval plus grand que votre seuil, et il y a les temps des échantillons juste avant ceux manquants.

La requête d'auto-jointure globale est la suivante. Je t'avais dit que c'était une boule de poils.

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)

Si vous devez le faire en production sur une grande table, vous voudrez peut-être le faire pour un sous-ensemble de vos données. Par exemple, vous pouvez le faire chaque jour pour les échantillons des deux jours précédents. Ce serait décemment efficace et garantirait également que vous n'oubliiez aucun échantillon manquant juste à minuit. Pour ce faire, vos petites tables virtuelles numérotées ressembleraient à ceci.

  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