Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Methode zum Auffinden von Lücken in Zeitreihendaten in MySQL?

Lassen Sie uns zunächst die Anzahl der Einträge pro Stunde in Ihrer Tabelle zusammenfassen.

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)

Wenn Sie nun alle sechs Minuten (zehnmal pro Stunde) etwas protokollieren, sollten alle Ihre Samplecount-Werte zehn sein. Dieser Ausdruck:CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) sieht haarig aus, aber es kürzt Ihre Zeitstempel einfach auf die Stunde, in der sie auftreten, indem Minute und Sekunde auf Null gesetzt werden.

Dies ist ziemlich effizient und wird Ihnen den Einstieg erleichtern. Es ist sehr effizient, wenn Sie Ihrer Spalte entry_time einen Index hinzufügen und Ihre Abfrage, sagen wir, auf die Beispiele von gestern beschränken können, wie hier gezeigt.

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)

Aber es ist nicht gut darin, ganze Stunden zu erkennen, die mit fehlenden Proben vergehen. Es ist auch ein wenig empfindlich gegenüber Jitter in Ihrem Sampling. Das heißt, wenn Ihre Probe zur vollen Stunde manchmal eine halbe Sekunde zu früh (10:59:30) und manchmal eine halbe Sekunde zu spät (11:00:30) ist, sind Ihre stündlichen Zusammenfassungszählungen falsch. Diese Stundenzusammenfassung (oder Tageszusammenfassung oder Minutenzusammenfassung usw.) ist also nicht kugelsicher.

Sie brauchen eine Self-Join-Abfrage, um alles richtig zu machen; es ist ein bisschen mehr ein Haarball und nicht annähernd so effizient.

Beginnen wir damit, uns selbst eine virtuelle Tabelle (Unterabfrage) wie diese mit nummerierten Beispielen zu erstellen. (Das ist ein Problem in MySQL; einige andere teure DBMS machen es einfacher. Egal.)

  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

Diese kleine virtuelle Tabelle gibt entry_num, entry_time, value.

Im nächsten Schritt verbinden wir es mit sich selbst.

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)

Dadurch werden die Tabellen um einen einzigen Eintrag versetzt aneinandergereiht, was durch die ON-Klausel des JOIN geregelt wird.

Schließlich wählen wir die Werte aus dieser Tabelle mit einem interval aus größer als Ihr Schwellenwert, und es gibt die Zeiten der Samples direkt vor den fehlenden.

Die allgemeine Self-Join-Abfrage ist dies. Ich habe dir doch gesagt, dass es ein Haarball war.

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)

Wenn Sie dies in der Produktion auf einer großen Tabelle tun müssen, möchten Sie dies möglicherweise für eine Teilmenge Ihrer Daten tun. Beispielsweise könnten Sie dies jeden Tag für die Proben der vorangegangenen zwei Tage tun. Dies wäre anständig effizient und würde auch sicherstellen, dass Sie keine fehlenden Samples direkt um Mitternacht übersehen. Um dies zu tun, würden Ihre kleinen virtuellen Tabellen mit Zeilennummerieren wie folgt aussehen.

  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