2009-05-18 26 views
5

Necesito hacer algo como:¿Cómo calculo una media móvil usando MySQL?

SELECT value_column1 
FROM table1 
WHERE datetime_column1 >= '2009-01-01 00:00:00' 
ORDER BY datetime_column1; 

Excepto además de value_column1, también necesito recuperar un moving average de los 20 valores anteriores de value_column1.

Se prefiere SQL estándar, pero usaré las extensiones de MySQL si es necesario.

Respuesta

18

Esto está justo fuera de mi cabeza, y estoy en el camino de la puerta, por lo que no ha sido probado. Tampoco puedo imaginar que funcionaría muy bien en cualquier tipo de gran conjunto de datos. Confirmé que al menos se ejecuta sin ningún error. :)

SELECT 
    value_column1, 
    (
    SELECT 
      AVG(value_column1) AS moving_average 
    FROM 
      Table1 T2 
    WHERE 
      (
       SELECT 
        COUNT(*) 
       FROM 
        Table1 T3 
       WHERE 
        date_column1 BETWEEN T2.date_column1 AND T1.date_column1 
     ) BETWEEN 1 AND 20 
    ) 
FROM 
    Table1 T1 
1

Cuando tuve un problema similar, terminé usando tablas temporales por una variedad de razones, ¡pero lo hizo mucho más fácil! Lo que hice se ve muy similar a lo que estás haciendo, en lo que respecta al esquema.

Haga que el esquema sea algo así como ID identity, start_date, end_date, value. Cuando seleccione, realice una acción de subselección de los 20 anteriores en función de la identificación de identidad.

Haga esto solo si ya se encuentra utilizando tablas temporales por otros motivos (pulso las mismas filas una y otra vez para diferentes métricas, por lo que fue útil tener el pequeño conjunto de datos).

+0

No estoy seguro de dónde entran las tablas temporales, puedo usar su solución sin ellas. Sin embargo, tiene el problema de la dependencia en la columna de identidad que es contigua. –

+0

La columna de identidad contigua es el punto central de la tabla temporal ... En mi caso, tengo años y años de datos, pero cada mes de datos se procesa solo. Extraigo datos en tablas temporales y realizo muchas métricas sobre ellos. El uso de tablas temporales (o funciones con valores de tabla) facilitó muchos aspectos del proceso, en mi caso. – overslacked

2

El enfoque de Tom H funcionará. Se puede simplificar como este si tiene una columna de identidad:

SELECT T1.id, T1.value_column1, avg(T2.value_column1) 
FROM table1 T1 
INNER JOIN table1 T2 ON T2.Id BETWEEN T1.Id-19 AND T1.Id 
+0

No sé acerca de MySQL, pero en MS SQL Server eso no funcionará. No se garantiza que las columnas de IDENTIDAD sean secuenciales o contiguas. –

+0

Serían, si no usa SET IDENTITY_INSERT ON, o elimina los precios? En este caso, podría mover los datos a una tabla temporal con una columna de identidad ordenada por fecha. – Andomar

+0

Estoy de acuerdo con Tom. Una IDENTIDAD (o en el lenguaje MySQL, una clave primaria autoincremento) puede no ser secuencial o contigua. ¿Qué sucede si elimina algunas filas del centro de la tabla? Tendría espacios en la llave. –

0

En mi experiencia, MySQL a partir del 5.5.x tiende a no utilizar índices en selecciona dependientes, si una subconsulta o unirse. Esto puede tener un impacto muy significativo en el rendimiento donde los criterios de selección dependientes cambian en cada fila.

La media móvil es un ejemplo de consulta que pertenece a esta categoría. El tiempo de ejecución puede aumentar con el cuadrado de las filas. Para evitar esto, elija un motor de base de datos que pueda realizar búsquedas indexadas en selecciones dependientes. Encuentro que Postgres funciona efectivamente para este problema.

1

Mi solución agrega un número de fila en la tabla. El siguiente código de ejemplo puede ayudar:

set @MA_period=5; 
select id1,tmp1.date_time,tmp1.c,avg(tmp2.c) from 
(select @b:[email protected]+1 as id1,date_time,c from websource.EURUSD,(select @b:=0) bb order by date_time asc) tmp1, 
(select @a:[email protected]+1 as id2,date_time,c from websource.EURUSD,(select @a:=0) aa order by date_time asc) tmp2 
where id1>@MA_period and id1>=id2 and id2>([email protected]_period) 
group by id1 
order by id1 asc,id2 asc 
+0

en caso de que use una condición para seleccionar registros específicos de la tabla (aquí se llama websource.EURUSD) debe usar exactamente la misma condición en ambas subselects (alias tmp1 y tmp2) –

1

Me doy cuenta de que esta respuesta es de aproximadamente 7 años demasiado tarde. Tenía un requisito similar y pensé que compartiría mi solución en caso de que sea útil para otra persona.

Existen algunas extensiones de MySQL para el análisis técnico que incluyen una media móvil simple. Son muy fáciles de instalar y de utilizar: https://github.com/mysqludf/lib_mysqludf_ta#readme

Una vez que haya instalado el UDF (siguiendo las instrucciones del README), que puede incluir una media móvil simple en una instrucción de selección como esta:

SELECT TA_SMA(value_column1, 20) AS sma_20 FROM table1 ORDER BY datetime_column1 
Cuestiones relacionadas