【问题标题】:calculate avg(value) for last 10 records postgresql计算最近 10 条记录 postgresql 的平均值(值)
【发布时间】:2021-11-20 10:40:09
【问题描述】:

我有一个棘手的任务, 假设我们有“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..... 请帮我解决这个任务

【问题讨论】:

  • 如何确定“后三名”?我的意思是什么决定了循环时间 15 是否在 14 之前?
  • @SalmanA 好的,我已经更新了我的问题并为表格添加了 id,所以我们需要对 id 进行降序排序,例如,对于轨道 1,最后一个圆圈结果是 15 和 id 10,第一个是 13,id 为 6
  • 平均值也不匹配,但我假设您只是输入了随机数。
  • OVER() , ROWS BETWEEN.. 中添加显式窗口规范

标签: sql postgresql average window-functions


【解决方案1】:

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

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

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

【讨论】:

  • CURRENT ROW 是默认帧结束,因此您可以将ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 缩短为ROWS 2 PRECEDING
【解决方案2】:

如果您只需要至少有 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

【讨论】:

  • current row 是默认帧结束,因此您可以将rows between 2 preceding and current row 缩短为rows 2 preceding
  • 同样,条件lag(id, 2) over(partition by TRACK, CAR order by id) is not null 可以缩短为row_number() over(partition by TRACK, CAR order by id) &gt; 2,因为你并不关心id 的值是什么,你只想检查至少有两个前面分区中的行。
  • 另外,这个问题要求的是赛道最后三个循环时间的平均值,与汽车无关,所以看起来CAR 不应该是分区的一部分 - 问题。
  • 您也可以使用window 子句来避免重复窗口定义。
【解决方案3】:

使用 LAED() 然后检查接下来的 2 行之一是否为 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

【讨论】:

    【解决方案4】:

    一些已发布的答案最终会重复窗口定义。您可以使用 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)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-01
      • 1970-01-01
      • 2016-11-14
      • 2018-12-24
      • 2019-12-27
      相关资源
      最近更新 更多