Home > Net >  calculate avg(value) for last 10 records postgresql
calculate avg(value) for last 10 records postgresql

Time:09-28

i have a tricky task, lets assume we have table "Racings", and there we have columns TRACK, CAR, CIRCLE_TIME here is an example how data could be look like:

id track car circle_time
10 1 10 15
9 1 10 14
8 1 10 16
7 1 10 15
6 1 10 13
5 2 10 7
4 2 10 4
3 2 10 5
2 3 10 8
1 3 10 10

what i need, i to add one more coumn like avg3_circle_time which will show me an average time from last 3 circle_time from each track, example:

id track car circle_time avg3_circle_time
10 1 10 15 15
9 1 10 14 15
8 1 10 16 14.6
7 1 10 15 null
6 1 10 13 null
5 2 10 7 5.3
4 2 10 4 null
3 2 10 5 null
2 3 10 8 null
1 3 10 10 null

I know how it could works in oracle, you could use something like rowid, but in case of postgresql i don't know, i have a draft like .....avg(circle_time) OVER(PARTITION BY track,car.....) as avg3_circle_time..... help me to solve that task please

CodePudding user response:

If you want only values when at least 3 circles available

select *
 , case when lag(id, 2) over(partition by TRACK, CAR order by id) is not null then 
        avg(CIRCLE_TIME) over(partition by TRACK, CAR order by id rows between 2 preceding and current row) end a
from Racing 
order by id desc;

db<>fiddle

Output

id  track   car circle_time a
10  1   10  15  15.0000000000000000
9   1   10  14  15.0000000000000000
8   1   10  16  14.6666666666666667
7   1   10  15  null
6   1   10  13  null
5   2   10  7   5.3333333333333333
4   2   10  4   null
3   2   10  5   null
2   3   10  8   null
1   3   10  10  null

CodePudding user response:

You can use window functions to calculate moving averages:

SELECT track, id, car, circle_time, AVG(circle_time) OVER (
  PARTITION BY track
  ORDER BY id
  ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
)
FROM t
ORDER BY track, id

Depending on your definition of previous three, the window could be ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING.

CodePudding user response:

Use avg as a window function with an explicit frame clause. SQL Fiddle

select *, avg(circle_time) 
over (partition by track order by id desc rows between CURRENT ROW and 2 following) as avg3_circle_time
from the_table;

CodePudding user response:

Use LAED() then checking one of the next 2 rows is NULL or not. THEN sum of three values for calculating average.

-- PostgreSQL
SELECT *
     , CASE WHEN next_circle_time IS NULL OR next_next_circle_time IS NULL
               THEN NULL
            ELSE ((t.circle_time   COALESCE(next_circle_time, 0)   COALESCE(next_next_circle_time, 0)) / 3 :: DECIMAL) :: DECIMAL(10, 1)
        END avg_circle_time
FROM (SELECT *
           , LEAD(circle_time, 1) OVER (PARTITION BY track ORDER BY id DESC) next_circle_time
           , LEAD(circle_time, 2) OVER (PARTITION BY track ORDER BY id DESC) next_next_circle_time
      FROM Racings) t

Another way Use AVG()

SELECT *
     , CASE WHEN LEAD(circle_time, 2) OVER (PARTITION BY track ORDER BY id DESC) IS NULL
                 OR LEAD(circle_time, 1) OVER (PARTITION BY track ORDER BY id DESC) IS NULL
               THEN NULL
            ELSE AVG(circle_time) OVER (PARTITION BY track ORDER BY id DESC ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING)
       END :: DECIMAL(10, 2) avg_circle_time       
FROM Racings

Please check from url where both query exists https://dbfiddle.uk/?rdbms=postgres_11&fiddle=f0cd868623725a1b92bf988cfb2deba3

  • Related