PostgreSQL计算每个组的排名以及其他统计数据
我有一张结构如下的桌子PostgreSQL计算每个组的排名以及其他统计数据,sql,postgresql,Sql,Postgresql,我有一张结构如下的桌子 |user_id | place | type_of_place | money_earned| time | |--------+-------+---------------+-------------+------| | | | | | | 这张桌子很大,有几百万行。数据位于PostgreSQL 9.1数据库中 我想计算每个用户的位置id和类型:平均值、标准偏差、按计数排序的前
|user_id | place | type_of_place | money_earned| time |
|--------+-------+---------------+-------------+------|
| | | | | |
这张桌子很大,有几百万行。数据位于PostgreSQL 9.1数据库中
我想计算每个用户的位置id和类型:平均值、标准偏差、按计数排序的前5个位置,以及最常用的时间模式
结果数据必须采用以下格式:
| user_id | type_of_place | avg | stddev | top5_places | mode |
+---------+---------------+-----+--------+------------------+------+
| 1 | tp1 | 10 | 1 | {p1,p2,p3,p4,p5} | 8 |
| 2 | tp1 | 3 | 2 | {p3,p4} | 23 |
| 1 | tp3 | 1 | 1 | {p1} | 4 |
等等
是否有一种方法可以有效地使用窗口函数执行此操作
如果我想按周分组怎么办?i、 e.表示周数的另一列
谢谢大家! 一个标准的分组查询将为您提供大部分信息:
SELECT
user_id,
type_of_place,
avg(money_earned) AS avg,
stddev(money_earned) AS stddev
FROM
earnings -- I'm not sure what your data table is called...
GROUP BY
user_id,
type_of_place
这将保留前5位和模式列。它们也是聚合,但不是标准PostgreSQL安装中定义的聚合。幸运的是,您可以添加它们
这是一个讨论如何定义模式聚合函数的页面:
一旦有了模式聚合函数,假设时间是某种时间戳,将添加到选择列表的表达式将是:
SELECT
...
mode(extract(hour FROM time)) AS mode -- Add this expression
FROM
...
接受货币订单
对于排名前5的位置,有几种方法,但最快的方法可能是使用PostgreSQL的内置array_agg函数,并使用前5个元素:
SELECT
...
(array_agg(place ORDER BY money_earned DESC))[1:5] AS top5_places -- Add this expression
FROM
...
另一种方法是定义另一个名为实例top5的聚合,它执行相同的功能。如果每个用户/类型的位置组合都有许多不同的位置,这可能会更有效,因为它可以在前5个位置之后停止累积,而上面的表达式通常会构建所有位置的完整数组,然后截断到前5个位置
这假设一个地方对于每个用户/类型组合都有一个唯一的收入条目。如果一个位置可以出现多次,并且您希望按每个位置的coursey_进行排序,那么您需要使用下面示例中的子查询
按计数排序
好的,那么位置应该根据它们出现的频率来排序。下面是一种快速方法,它使用了两个子查询-将其作为表达式添加到上述查询的select子句中:
(SELECT
(array_agg(place ORDER BY cnt DESC))[1:5]
FROM
(SELECT place, count(*) FROM earnings AS t2
WHERE t2.user_id = earnings.user_id AND t2.type_of_place = earnings.type_of_place
GROUP BY place) AS s (place, cnt)
) AS top5_places
名为s的内部子查询计算出该用户/类型组合的每个位置的表,以及它发生的次数,我称之为cnt。然后按该计数的降序将这些数据馈送到数组_agg
我想可能会有更整洁、更高效的写作方法。如果没有,那么我建议尝试将这个复杂的表达式移动到函数或聚合中,如果可以的话
每小时地点的历史记录
我们将使用类似的表达式,它将返回按小时排序的计数数组:
(SELECT
array_agg(cnt ORDER BY hour DESC)
FROM
(SELECT extract(hour FROM time), count(*) FROM earnings AS t2
WHERE t2.user_id = earnings.user_id AND t2.type_of_place = earnings.type_of_place
GROUP BY 1) AS s (hour, cnt)
) AS hourly_histogram
将其添加到原始查询的select子句中。对于avg,stddev;这些是挣钱栏的平均值和标准偏差吗?排名前5位的排名是按收入从高到低排列的吗?我不知道“按计数排序”是什么意思。谢谢@Edmund,让我试着说得更具体一些,我想说的是“按计数排序”是指我希望他们按计数的方式,按出现的次数进行排序。因此数组中的第一个元素是显示最多的元素,等等。谢谢@Edmund!你能按它出现的次数对已排序的位置进行更改吗,count?提前谢谢!嘿,我添加了一个部分来尝试这样做。我的日子有点晚了,而且不太好,所以如果有更好的方式,我不会感到惊讶…:再次感谢@Edmund,我不知道是否有更好或更优雅的方法,让我试试,我会回到你身边的……:你好@Edmund,我发现了一个小问题,数组\u aggplace ORDER BY money\u earned DESC code返回了重复项。。。使用distinct inside并不能解决问题,实际上会抛出一个错误。。。你知道怎么解决吗?嗨,再次@Edmund,另一个问题,如果不是模式,而是我想为每个用户提取一种直方图呢?我的意思是,对于用户1,我将有{2,0,4,5,0,0,0,…},其中数组的位置表示小时0-23,数字表示频率?如果你认为这是值得的,我打开另一个问题没有问题,我会这样做。