Reputation: 967
I have a table "my_table" with 2 columns
1. Timestamp (ts)
2. Value (val)
I need to return for each timestamp, the average of 10 latest values that occur on or before the timestamp.
I am able to do something similar with the following query:
SELECT t2.ts as time,
AVG(t1.val) as avg_val
FROM table_name as t1, table_name as t2
WHERE t1.ts <= t2.ts
This returns a timestamp and the average of all the values that occur on or before that timestamp. I need to modify this query to return the average of the 10 latest values and not all the values.
PS: Apologies for a bad question title. Was not sure how to frame it.
Upvotes: 0
Views: 54
Reputation: 1269773
There is not an easy way to express this query in MySQL prior to version 8 -- even using variables. I'm not going to vouch for performance, but this should do what you want:
SELECT t1.ts, t1.val, AVG(t2.val)
FROM t t1 LEFT JOIN
t t2
ON t2.ts <= t.ts AND
t2.ts >= (SELECT t3.ts
FROM t t3
WHERE t3.ts <= t1.ts
ORDER BY t3.ts DESC
LIMIT 9, 1
)
GROUP BY t1.ts, t1.val;
Of course, in 8+, this is simply:
select t.*,
avg(t.val) over (order by ts rows between 9 preceding and current row) as avg_10
from t;
Upvotes: 0
Reputation: 272106
Ignore previous answer. You can use "poor man's rank" to rank the rows sorted by timestamp. You can join the result by itself for find previous 9 rows for each rank:
SELECT a.ts, AVG(c.val)
FROM (
SELECT t1.ts, COUNT(t2.ts) AS rank
FROM t AS t1
INNER JOIN t AS t2 ON t2.ts <= t1.ts
GROUP BY t1.ts
) AS a
INNER JOIN (
SELECT t1.ts, COUNT(t2.ts) AS rank
FROM t AS t1
INNER JOIN t AS t2 ON t2.ts <= t1.ts
GROUP BY t1.ts
) AS b ON b.rank BETWEEN a.rank - 9 AND a.rank
INNER JOIN t AS c ON b.ts = c.ts
GROUP BY a.ts
I understand it is a bit clunky but it could be better than correlated sub queries under certain conditions.
Upvotes: 2