Hier ist ein Ansatz.
Beginnen Sie damit, die Statuszeilen nach Zeitstempel zu ordnen (Inline-Ansicht mit dem Alias s
). Verwenden Sie dann MySQL-Benutzervariablen, um die Werte aus vorherigen Zeilen beizubehalten, während Sie jede Zeile durchlaufen.
Was wir wirklich suchen, ist ein „Up“-Status, der unmittelbar auf eine Folge von „Down“-Status folgt. Und wenn wir diese Zeile mit dem Status „aktiv“ finden, brauchen wir wirklich den frühesten Zeitstempel aus der vorangegangenen Reihe mit dem Status „down“.
Also, so etwas wird funktionieren:
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
Dies funktioniert für den bestimmten Datensatz, den Sie anzeigen.
Was dies nicht handhabt (was es nicht zurückgibt) ist eine 'down'-Periode, die noch nicht beendet ist, dh eine Folge von 'down'-Status ohne nachfolgenden 'up'-Status.
Um eine Filesort-Operation zu vermeiden, um die Zeilen der Reihe nach zurückzugeben, benötigen Sie einen abdeckenden Index für (time,status)
. Diese Abfrage generiert eine temporäre (MyISAM)-Tabelle, um die Inline-Ansicht mit dem Alias d
zu materialisieren .
HINWEIS: Um zu verstehen, was diese Abfrage tut, ziehen Sie diese äußerste Abfrage ab und führen Sie nur die Abfrage für die Inline-Ansicht mit dem Alias d
aus (Sie können s.time
hinzufügen zur Auswahlliste.)
Diese Abfrage ruft jede Zeile mit dem Status „Up“ oder „Down“ ab. Der "Trick" besteht darin, dass sowohl eine "Start"- als auch eine "Ende"-Zeit (Markierung einer Down-Periode) nur für die Zeilen zugewiesen wird, die eine "Down"-Periode beenden. (Das heißt, die erste Zeile mit dem Status „Up“ folgt Zeilen mit dem Status „Down“.) Hier wird die eigentliche Arbeit erledigt, die äußerste Abfrage filtert einfach alle „zusätzlichen“ Zeilen in dieser Ergebnismenge heraus (die wir brauche ich nicht.)
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
Der Zweck der Inline-Ansicht, aliasiert als s
ist es, die Zeilen nach dem Zeitstempelwert zu ordnen, damit wir sie der Reihe nach verarbeiten können. Die Inline-Ansicht mit dem Alias i
ist nur da, damit wir einige Benutzervariablen zu Beginn der Abfrage initialisieren können.
Wenn wir auf Oracle oder SQL Server laufen würden, könnten wir „analytische Funktionen“ oder „Ranking-Funktionen“ (wie sie genannt werden) verwenden. MySQL bietet so etwas nicht, also müssen wir „unsere eigenen rollen“. ".