Sql 计算每个影响者随时间的跟随者增长

Sql 计算每个影响者随时间的跟随者增长,sql,database,postgresql,aggregate-functions,greatest-n-per-group,Sql,Database,Postgresql,Aggregate Functions,Greatest N Per Group,我每天都有一张有影响力的人和他们的追随者计数器的表格: influencer_id | date | followers 1 | 2020-05-29 | 7361 1 | 2020-05-28 | 7234 ... 2 | 2020-05-29 | 82 2 | 2020-05-28 |

我每天都有一张有影响力的人和他们的追随者计数器的表格:

influencer_id |     date     |    followers
     1        | 2020-05-29   |      7361
     1        | 2020-05-28   |      7234
                    ...
     2        | 2020-05-29   |       82
     2        | 2020-05-28   |       85
                    ...
     3        | 2020-05-29   |      3434
     3        | 2020-05-28   |      2988
     3        | 2020-05-27   |      2765
                    ...
假设我想计算每个影响者在过去7天内获得了多少追随者,并得到下表:

influencer_id |                       growth
     1        |  <num followers last day - num followers first day>
     2        |                         "
     3        |                         "
作为第一次尝试,我这样做了:

SELECT influencer_id,
      (MAX(followers) - MIN(followers)) AS growth
FROM influencer_follower_daily
WHERE date < '2020-05-30'
AND date >= '2020-05-23'
GROUP BY influencer_id;
这是有效的,并显示了每个影响者一周内的增长情况。但是它假设跟随者的数量总是增加,并且人们从不放弃跟随

那么,有没有一种方法可以在原始表上使用SQL查询来实现我想要的呢?或者我必须使用FOR循环生成一个全新的表,该循环计算每个日期之间的+/-follower change列?

Postgres没有第一个/最后一个聚合函数。一种方法是:

SELECT DISTINCT influencer_id,
       ( FIRST_VALUE(followers) OVER (PARTITION BY influencer_id ORDER BY DATE DESC) -
         FIRST_VALUE(followers) OVER (PARTITION BY influencer_id ORDER BY DATE ASC)
       ) as growth
FROM influencer_follower_daily
WHERE date < '2020-05-30' AND date >= '2020-05-23';
另一种选择是使用阵列:

SELECT influencer_id,
       ( ARRAY_AGG(followers ORDER BY DATE DESC) )[1] -
         ARRAY_AGG(followers ORDER BY DATE ASC) )[1]
       ) as growth
FROM influencer_follower_daily
WHERE date < '2020-05-30' AND date >= '2020-05-23'
GROUP BY influencer_id;

简单的聚合函数first和last没有在标准Postgres中实现。但见下文

1.数组_agg 一个带有array_agg的查询,但这比需要的更昂贵,尤其是对于每个组有许多行的查询。如果调用两次,并且按照每个聚合的顺序调用,则情况会更糟。这一等效替代方案应大大加快:

使用单一窗口功能,使用相同的排序顺序!作为主查询。要实现这一点,我们需要非默认窗口定义,其中的行位于。。。见:

并且在上而不是在下。见:

3.自定义聚合函数 始终如一 你可以自己添加,这很简单。看见 或者使用更快的C实现安装

相关的:

然后您的查询变得更简单:

SELECT influencer_id, last(followers) - first(followers) AS growth
FROM  (
   SELECT influencer_id, followers
   FROM   influencer_follower_daily 
   WHERE  date >= '2020-03-02'
   AND    date <  '2020-05-09'
   ORDER  BY influencer_id, date
   ) z
GROUP  BY influencer_id
ORDER  BY influencer_id;
适用于任何数值类型或任何运算符类型为-type的类型返回相同类型的类型。查询更简单,但:

SELECT influencer_id, growth(followers)
FROM  (
   SELECT influencer_id, followers
   FROM   influencer_follower_daily 
   WHERE  date >= '2020-05-23'
   AND    date <  '2020-05-30'
   ORDER  BY influencer_id, date
   ) z
GROUP  BY influencer_id
ORDER  BY influencer_id;
或者稍微慢一点,但最终很短:

SELECT influencer_id, growth(followers ORDER BY date)
FROM   influencer_follower_daily 
WHERE  date >= '2020-05-23'
AND    date <  '2020-05-30'
GROUP  BY 1
ORDER  BY 1;
小提琴

4.每个组多行的性能优化 由于每个组/分区有许多行,其他查询技术可以快得多。这方面的技术:

如果适用,我建议您开始一个新的问题,公开确切的表定义和基数

密切相关:


这管用!顺便说一下,在第一个查询中,在DATE ASC之后意外添加了一个减号。我试图编辑它,但编辑太小。@ddriver1:我对提供的查询的相对性能感兴趣。您是否可以运行EXPLAIN ANALYZE,对每5个最佳值进行计时,以升级缓存工件并添加一个答案?加上表和索引的定义和基数,如果这不是太多的问题。非常感谢您难以置信的详细答案。该表在列ID和influencer_ID上有索引,这是一个单独influencer表的FK。其他栏目是followers和day。对于你提供的每一个问题,我都以10分的最好成绩回答。它们都产生了正确的结果,并在某个点对66293行进行了排序。阵列增益:103.2ms2。独特+窗口:137.1ms 3。第一个和最后一个使用可移植SQL而不是C:256.3ms 4。自定义聚合增长:166.8毫秒5。戈登的独特+窗口:284。4ms@ddriver1:谢谢你带着测试结果回来!就像我预料的那样。
CREATE OR REPLACE FUNCTION f_growth(anyarray)
  RETURNS anyelement LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE AS
'SELECT $1[array_upper($1, 1)] - $1[1]';

CREATE OR REPLACE AGGREGATE growth(anyelement) (
   SFUNC     = array_append
 , STYPE     = anyarray
 , FINALFUNC = f_growth
 , PARALLEL  = SAFE
);
SELECT influencer_id, growth(followers)
FROM  (
   SELECT influencer_id, followers
   FROM   influencer_follower_daily 
   WHERE  date >= '2020-05-23'
   AND    date <  '2020-05-30'
   ORDER  BY influencer_id, date
   ) z
GROUP  BY influencer_id
ORDER  BY influencer_id;
SELECT influencer_id, growth(followers ORDER BY date)
FROM   influencer_follower_daily 
WHERE  date >= '2020-05-23'
AND    date <  '2020-05-30'
GROUP  BY 1
ORDER  BY 1;