Postgres 窗口函数:对满足基于当前行的条件的窗口框架的记录应用聚合函数

Posted

技术标签:

【中文标题】Postgres 窗口函数:对满足基于当前行的条件的窗口框架的记录应用聚合函数【英文标题】:Postgres window functions: applying an aggregate function on a window frame's records that satisfy a condition based on the current row 【发布时间】:2020-12-25 15:48:03 【问题描述】:

这个问题是关于 Postgres 12 的 SQL 语法的。

假设有一个stock_prices 表,其中包含以下列:tickerdateprice

我有兴趣在前 10 天的窗口上执行计算,例如:

SELECT 
  ticker,
  date, 
  price, 
  AVG(price) 
    OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN 10 PRECEDING AND 1 PRECEDING) as avg_previous_10d
FROM stock_prices

除了上面的简单平均计算之外,我还想对前 10 天窗口内满足当前记录相关条件的记录执行计算。具体来说,在前 10 天窗口内:

计算价格高于当前行价格的次数 计算高于当前行价格的平均价格 查找价格第一次高于当前行的价格

等等。

我知道我可以执行自联接,例如对this question 的回答。我的问题是 - 有没有一种使用窗口函数语法的简单方法来做到这一点?还是只有自行加入?

谢谢!

【问题讨论】:

【参考方案1】:

听起来横向连接可能是最好的方法。以下假设您每天有一行:

SELECT sp.*, sp2.*
FROM stock_prices sp CROSS JOIN LATERAL
     (SELECT AVG(sp2.price) as avg_previous_10d,
             COUNT(*) FILTER (WHERE sp2.price > sp.price) as cnt_highest_previous_10day,
             AVG(sp2.price) FILTER (WHERE sp2.price > sp.price) as avg_higher_previous_10day
      FROM stock_prices sp2
      WHERE sp2.ticker = sp.ticker AND
            sp2.date >= sp.date - INTERVAL '10 DAY' AND
            sp2.date < sp.date
     ) sp2;

这不是 100% 等同于您的查询,因为它使用 日历 时间。这可能是一个优势。但如果你想要完全等价的版本:

SELECT sp.*, sp2.*
FROM stock_prices sp CROSS JOIN LATERAL
     (SELECT AVG(sp2.price) as avg_previous_10d,
             COUNT(*) FILTER (WHERE sp2.price > sp.price) as cnt_highest_previous_10day,
             AVG(sp2.price) FILTER (WHERE sp2.price > sp.price) as avg_higher_previous_10day
      FROM (SELECT sp2.*
            FROM stock_prices sp2
            WHERE sp2.ticker = sp.ticker AND
                  sp2.date < sp.date
            ORDER BY sp2.date DESC
            LIMIT 10
           ) sp2
     ) sp2;

【讨论】:

【参考方案2】:

你为什么不用self join如下:

SELECT count(1) as num_times,
       Avg(s10.price) as avg_price,
       Min(s10.date) as first_time_higher_price     
  FROM stock_prices scur
  Join stock_prices s10 on s10.ticker = scur.ticker
 Where scur.date = current_date
   And s10.date >= current_date - interval '10 day' 
   And s10.date < current_date
   And s10.price >= scur.price

【讨论】:

以上是关于Postgres 窗口函数:对满足基于当前行的条件的窗口框架的记录应用聚合函数的主要内容,如果未能解决你的问题,请参考以下文章

postgres —— 窗口函数入门

基于当前行上方或下方的第 n 行的条件 - Python

Postgres - 如何对窗口函数列的每 x 行求和?

过去 30 天的平均值,不包括当前记录(混合日期和基于行的条件)

R语言data.table导入数据实战: .N函数和.I函数使用.N函数返回行数使用.I函数返回满足特定条件行的索引

如何在带有 Postgres 的动态框架中使用窗口函数中的列值?