Oto jedno podejście.
Zacznij od uporządkowania wierszy stanu według sygnatury czasowej (widok wbudowany z aliasem s
). Następnie użyj zmiennych użytkownika MySQL, aby zachować wartości z poprzednich wierszy podczas przetwarzania każdego wiersza.
To, czego naprawdę szukamy, to status „w górę”, który natychmiast następuje po sekwencji stanów „w dół”. A kiedy znajdziemy wiersz ze stanem „w górę”, naprawdę potrzebujemy najwcześniejszego znacznika czasu z poprzedniej serii stanu „w dół”.
Tak więc zadziała coś takiego:
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
Działa to dla konkretnego wyświetlanego zestawu danych.
To, czego to nie obsługuje (czego nie zwraca), to okres „nieaktywny”, który jeszcze się nie zakończył, to znaczy sekwencja statusu „nie działa” bez następującego statusu „w górę”.
Aby uniknąć operacji sortowania plików, która zwraca wiersze w kolejności, będziesz potrzebować indeksu obejmującego (time,status)
. To zapytanie wygeneruje tymczasową tabelę (MyISAM), aby zmaterializować widok wbudowany aliasem d
.
UWAGA: Aby zrozumieć, co robi to zapytanie, oderwij to najbardziej zewnętrzne zapytanie i uruchom tylko zapytanie dla widoku wbudowanego o aliasie d
(możesz dodać s.time
do listy wyboru.)
To zapytanie pobiera każdy wiersz ze statusem „w górę” lub „w dół”. „Sztuczka” polega na tym, że przypisuje zarówno czas „początku”, jak i „zakończenia” (oznaczenie okresu spadku) tylko w wierszach, które kończą okres „w dół”. (Oznacza to, że pierwszy wiersz ze statusem „w górę” następujący po wierszach ze statusem „w dół”.) W tym miejscu wykonywana jest prawdziwa praca, najbardziej zewnętrzne zapytanie po prostu odfiltrowuje wszystkie „dodatkowe” wiersze w tym zestawie wyników (które nie trzeba.)
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
Przeznaczenie wbudowanego widoku aliasowanego jako s
jest uzyskanie wierszy uporządkowanych według wartości znacznika czasu, abyśmy mogli przetwarzać je po kolei. Widok wbudowany aliasem i
jest tylko tam, abyśmy mogli zainicjować niektóre zmienne użytkownika na początku zapytania.
Gdybyśmy pracowali na Oracle lub SQL Server, moglibyśmy skorzystać z „funkcji analitycznych” lub „funkcji rankingowych” (jak się je nazywa). MySQL nie zapewnia niczego takiego, więc musimy „zrobić własne ".