计算最后 10 条记录的平均值(值)postgresql

calculate avg(value) for last 10 records postgresql

我有一个棘手的任务, 假设我们有 table 个“Racings”,并且我们有 TRACK、CAR、CIRCLE_TIME 列 这是一个数据的示例:

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

我需要的是,我再添加一个像 avg3_circle_time 这样的列,它会显示每首曲目的最后 3 circle_time 的平均时间,例如:

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

我知道它在 oracle 中是如何工作的,你可以使用像 rowid 这样的东西,但如果是 postgresql 我不知道,我有一个像 .....avg(circle_time) 这样的草稿OVER(PARTITION BY track,car.......) as avg3_circle_time..... 请帮我解决这个任务

您可以使用window函数来计算移动平均线:

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

根据您对前三个的定义,window 可能是 ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING

如果您只需要至少有 3 个圆圈可用的值

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

输出

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

使用 LAED() 然后检查接下来的两行之一是否为 NULL。然后计算平均值的三个值的总和。

-- 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

另一种方法使用 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

请从 url 检查 两个查询 是否存在 https://dbfiddle.uk/?rdbms=postgres_11&fiddle=f0cd868623725a1b92bf988cfb2deba3

几个已发布的答案最终重复了 window 定义。您可以使用 window 子句避免这种情况:

select *, 
   case when row_number() over(track_window) > 2 
        then trunc(avg(CIRCLE_TIME) over(track_window rows 2 preceding), 1)
   end a
from Racing 
window track_window as (partition by track order by id)
order by id desc

请注意,在此示例中,track_window 是如何定义一次,然后为 row_numberavg 重新使用的。在后一种情况下,window 子句也用框架修饰 (rows 2 preceding)。