Aquí hay un enfoque.
Comience por obtener las filas de estado en orden por marca de tiempo (vista en línea con alias como s
) ). Luego use las variables de usuario de MySQL para mantener los valores de las filas anteriores, mientras procesa cada fila.
Lo que realmente estamos buscando es un estado 'alto' que sigue inmediatamente a una secuencia de estado 'abajo'. Y cuando encontramos esa fila con el estado 'activo', lo que realmente necesitamos es la marca de tiempo más antigua de la serie anterior de estado 'inactivo'.
Entonces, algo como esto funcionará:
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
Esto funciona para el conjunto de datos particular que muestra.
Lo que esto no maneja (lo que no devuelve) es un período 'inactivo' que aún no finaliza, es decir, una secuencia de estados 'inactivos' sin un estado 'activo' posterior.
Para evitar una operación de clasificación de archivos para devolver las filas en orden, querrá un índice de cobertura en (time,status)
. Esta consulta generará una tabla temporal (MyISAM) para materializar la vista en línea con alias d
.
NOTA: Para comprender lo que está haciendo esta consulta, elimine esa consulta más externa y ejecute solo la consulta para la vista en línea con el alias d
(puede agregar s.time
a la lista de selección.)
Esta consulta obtiene cada fila con un estado 'arriba' o 'abajo'. El "truco" es que está asignando un tiempo de "inicio" y "finalización" (marcando un período de inactividad) solo en las filas que finalizan un período de "inactividad". (Es decir, la primera fila con un estado 'arriba' que sigue a las filas con un estado 'abajo'). Aquí es donde se realiza el trabajo real, la consulta más externa simplemente filtra todas las filas "adicionales" en este conjunto de resultados (que no es necesario.)
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
El propósito de la vista en línea con alias como s
es obtener las filas ordenadas por valor de marca de tiempo, para que podamos procesarlas en secuencia. La vista en línea con alias como i
está ahí para que podamos inicializar algunas variables de usuario al comienzo de la consulta.
Si estuviéramos ejecutando en Oracle o SQL Server, podríamos hacer uso de "funciones analíticas" o "funciones de clasificación" (como se les llama, respectivamente). MySQL no proporciona nada de eso, por lo que tenemos que "hacer nuestro propio ".