Her er én tilgang.
Start med at få statusrækkerne i rækkefølge efter tidsstempel (indbygget visning kaldet s
). Brug derefter MySQL-brugervariabler til at beholde værdierne fra tidligere rækker, mens du behandler hver række.
Det, vi virkelig leder efter, er en 'op'-status, der umiddelbart følger en sekvens af 'ned'-status. Og når vi finder den række med "op"-status, er det, vi virkelig har brug for, det tidligste tidsstempel fra den foregående serie af "ned"-status.
Så noget som dette vil virke:
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
Dette virker for det bestemte datasæt, du viser.
Hvad dette ikke håndterer (hvad det ikke returnerer) er en 'ned'-periode, der endnu ikke er afsluttet, det vil sige en sekvens af 'ned'-status uden følgende 'op'-status.
For at undgå en filsorteringsoperation for at returnere rækkerne i rækkefølge, vil du have et dækkende indeks på (time,status)
. Denne forespørgsel vil generere en midlertidig (MyISAM) tabel for at materialisere den indbyggede visning kaldet d
.
BEMÆRK: For at forstå, hvad denne forespørgsel gør, skal du fjerne den yderste forespørgsel og kun køre forespørgslen for den indbyggede visning kaldet d
(du kan tilføje s.time
til den valgte liste.)
Denne forespørgsel får hver række med statussen "op" eller "ned". "Tricket" er, at det kun tildeler både et "start"- og "slut"-tidspunkt (markerer en ned-periode) på de rækker, der slutter en "ned"-periode. (Det vil sige, den første række med en 'op'-status følger rækker med en 'ned'-status.) Det er her det rigtige arbejde udføres, den yderste forespørgsel filtrerer bare alle de "ekstra" rækker fra i dette resultatsæt (som vi behøver ikke.)
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
Formålet med inline-visning kaldet s
er at få rækkerne ordnet efter tidsstempelværdi, så vi kan behandle dem i rækkefølge. Den indbyggede visning kaldet i
er der bare, så vi kan initialisere nogle brugervariable i starten af forespørgslen.
Hvis vi kørte på Oracle eller SQL Server, kunne vi gøre brug af henholdsvis "analytiske funktioner" eller "rangeringsfunktioner" (som de hedder.) MySQL giver ikke noget lignende, så vi skal "rulle vores egne" ".