sql >> Base de Datos >  >> RDS >> Mysql

SQL:encuentre todos los tiempos de inactividad y la duración de los tiempos de inactividad de los datos de MySQL (conjunto de filas con marcas de tiempo y mensajes de estado)

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 ".