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

Detectar elementos consecutivos que cumplan criterios particulares en una serie de tiempo

Mi enfoque para esto:comenzar con la serie temporal de observaciones y darle a cada una un número de serie.

Esta numeración de serie es un dolor de cabeza en MySQL, pero no importa. Dada una tabla con una columna ts (un elemento de fecha y hora) y una columna temporal, aquí está la consulta para obtenerlos con números de serie.

SELECT @sample:[email protected]+1 AS ser, ts, temp
  FROM (
     SELECT ts,temp
       FROM t
      ORDER BY ts
    ) C,
  (SELECT @sample:=0) s 

Eche un vistazo a este sqlfiddle:http://sqlfiddle.com/#!2/ d81e2/5/0

OK, eso es bastante trivial. Ahora, digamos que estamos buscando períodos de tiempo en los que la temperatura es de 25 grados o más. Para hacer esto, necesitamos cortar la serie temporal para que omita esas observaciones. Eso dice así:

SELECT @sample:[email protected]+1 AS ser, ts, temp
  FROM (
     SELECT ts,temp
       FROM t
      WHERE NOT temp >= 25
      ORDER BY ts
    ) C,
  (SELECT @sample:=0) s

Aquí está el sqlfiddle:http://sqlfiddle.com/#!2/d81e2/6 /0

Ahora el siguiente truco es encontrar los intervalos de tiempo en esta secuencia. Podemos usar la técnica de esta publicación SO para hacer eso. Método de encontrar lagunas en los datos de series temporales en MySQL?

Siguiente paso, lo unimos a sí mismo.

SELECT two.ser, two.ts, two.temp, 
       TIMESTAMPDIFF(MINUTE, two.ts, one.ts) gap
  FROM (
     /* virtual table */
  ) ONE
  JOIN (
     /* same virtual table */
  ) TWO ON (TWO.ser+ 1 = ONE.ser)

Esta consulta obtiene el intervalo de tiempo entre cada elemento de la serie y el siguiente. Es algo sencillo de hacer conceptualmente, pero complicado en la versión MySQL de SQL. Aquí está la consulta completa.

SELECT two.ser, two.ts, two.temp, 
       TIMESTAMPDIFF(MINUTE, two.ts, one.ts) gap
      FROM (
 SELECT @sample:[email protected]+1 AS ser, ts, temp
  FROM (
     SELECT ts,temp
       FROM t
      WHERE NOT temp >= 25
      ORDER BY ts
    ) C,
  (SELECT @sample:=0) s
      ) ONE
      JOIN (
SELECT @sample2:[email protected]+1 AS ser, ts, temp
  FROM (
     SELECT ts,temp
       FROM t
      WHERE NOT temp >= 25
      ORDER BY ts
    ) C,
  (SELECT @sample2:=0) s
      ) TWO ON (TWO.ser+ 1 = ONE.ser)

Aquí está el sqlfiddle:http://sqlfiddle.com/#!2/d81e2/13 /0 Tenga en cuenta que algunos de los espacios tienen una duración de 30 minutos. Eso es normal para lecturas consecutivas. Algunos son de 60 minutos. Eso también es normal, porque la serie de tiempo que estoy usando tiene algunas entradas faltantes. Las entradas en este conjunto de resultados muestran los tiempos y las temperaturas inmediatamente antes de los espacios.

Entonces, todo lo que queda es deshacerse de los huecos basura (30 y 60 minutos) y luego ordenar los huecos restantes en orden descendente.

SELECT two.ts, two.temp, 
       TIMESTAMPDIFF(MINUTE, two.ts, one.ts) gap
      FROM (
 SELECT @sample:[email protected]+1 AS ser, ts, temp
  FROM (
     SELECT ts,temp
       FROM t
      WHERE NOT temp >= 25
      ORDER BY ts
    ) C,
  (SELECT @sample:=0) s
      ) ONE
      JOIN (
SELECT @sample2:[email protected]+1 AS ser, ts, temp
  FROM (
     SELECT ts,temp
       FROM t
      WHERE NOT temp >= 25
      ORDER BY ts
    ) C,
  (SELECT @sample2:=0) s
      ) TWO ON (TWO.ser+ 1 = ONE.ser)
 WHERE TIMESTAMPDIFF(MINUTE, two.ts, one.ts)> 60
 ORDER BY TIMESTAMPDIFF(MINUTE, two.ts, one.ts) DESC

Esto da una fila para cada secuencia de tiempo donde la temperatura está por encima de los 25 grados; el tiempo más largo primero. El elemento que se muestra en el conjunto de resultados es la última vez que la temperatura estuvo por debajo de 25 antes de que subiera. Violín SQL. http://sqlfiddle.com/#!2/d81e2/14/0

Divertido, ¿eh?