Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Detectar itens consecutivos que atendem a critérios específicos em uma série temporal


Minha abordagem para isso:comece com a série temporal de observações e dê a cada uma um número de série.

Esta numeração de série é uma dor de cabeça no MySQL, mas não importa. Dada uma tabela com uma coluna ts (um item de data e hora) e uma coluna temporária, aqui está a consulta para obtê-los com números de série.
SELECT @sample:[email protected]+1 AS ser, ts, temp
  FROM (
     SELECT ts,temp
       FROM t
      ORDER BY ts
    ) C,
  (SELECT @sample:=0) s 

Dê uma olhada neste sqlfiddle:http://sqlfiddle.com/#!2/ d81e2/5/0

OK, isso é bem trivial. Agora, digamos que estamos procurando períodos de tempo em que a temperatura é de 25 graus ou mais. Para fazer isso, precisamos cortar a série temporal para que ela omita essas observações. Isso fica assim:
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

Aqui está o sqlfiddle:http://sqlfiddle.com/#!2/d81e2/6 /0

Agora, o próximo truque é encontrar os intervalos de tempo nesta sequência. Podemos usar a técnica deste post SO para fazer isso. Método de encontrar lacunas nos dados de séries temporais no MySQL?

Próximo passo, nós o juntamos a si mesmo.
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)

Essa consulta obtém o intervalo de tempo entre cada item da série e o seguinte. É uma coisa simples de fazer conceitualmente, mas complicada na versão MySQL do SQL. Aqui está a 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)

Aqui está o sqlfiddle:http://sqlfiddle.com/#!2/d81e2/13 /0 Observe que alguns dos intervalos têm 30 minutos de duração. Isso é normal para leituras consecutivas. Alguns são 60 minutos. Isso também é normal, porque a série temporal que estou usando tem algumas entradas ausentes. As entradas neste conjunto de resultados mostram os tempos e temperaturas imediatamente antes dos intervalos.

Então, tudo o que resta é se livrar das lacunas de lixo (30 e 60 minutos) e depois ordenar as lacunas restantes em ordem decrescente.
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

Isso dá uma linha para cada sequência de tempo em que a temperatura está acima de 25 graus; o maior tempo primeiro. O item mostrado no conjunto de resultados é a última temperatura abaixo de 25 antes de subir. violino SQL. http://sqlfiddle.com/#!2/d81e2/14/0

Divertido, hein?