简单的聚合函数 first() 和 last() 在标准 Postgres 中没有实现。但见下文。
1。 array_agg()
Gordon demonstrated 与 array_agg() 的查询,但这比必要的成本更高,尤其是每个组有很多行。当调用两次时更是如此,并且每个聚合使用ORDER BY。这种等效的替代方案应该明显更快:
SELECT influencer_id, arr[array_upper(arr, 1)] - arr[1]
FROM (
SELECT influencer_id, array_agg(followers) AS arr
FROM (
SELECT influencer_id, followers
FROM influencer_follower_daily
WHERE date >= '2020-05-23'
AND date < '2020-05-30'
ORDER BY influencer_id, date
) sub1
GROUP BY influencer_id
) sub2;
因为它排序一次并聚合一次。内部子查询sub1 的排序顺序被传递到下一个级别。见:
索引很重要:
如果您查询整个表或大部分表,(influencer_id, date, followers) 上的索引 可以(很大)帮助进行仅索引扫描。
如果您只查询表的一小部分,(date) 或 (date, influencer_id, followers) 上的索引 会有所帮助(很多)。
2。 DISTINCT & 窗口函数
Gordon 还演示了 DISTINCT 的窗口函数。同样,可以明显更快:
SELECT DISTINCT ON (influencer_id)
influencer_id
, last_value(followers) OVER (PARTITION BY influencer_id ORDER BY date
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
- followers AS growth
FROM influencer_follower_daily
WHERE date >= '2020-05-23'
AND date < '2020-05-30'
ORDER BY influencer_id, date;
使用 single 窗口函数,使用与主查询相同的排序顺序 (!)。为此,我们需要使用ROWS BETWEEN ... 定义非默认窗口,请参阅:
还有DISTINCT ON 而不是DISTINCT。见:
3。自定义聚合函数
first() 和 last()
你可以自己添加,很简单。见instructions in the Postgres Wiki。
或者安装additional module first_last_agg,在 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;
自定义聚合growth()
您可以将first() 和last() 组合在一个聚合函数中。这样更快,但调用两个 C 函数仍将优于一个自定义 SQL 函数。
基本上将我的第一个查询的逻辑封装在一个自定义聚合中:
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
);
适用于任何数字类型(或任何带有运算符type - 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;
db小提琴here
4。每组许多行的性能优化
每个组/分区有许多行,其他查询技术可以(很多)更快。这些方面的技术:
如果适用,我建议您开始一个新问题,披露确切的表定义和基数...
密切相关: