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,数字表示频率?如果你认为这是值得的,我打开另一个问题没有问题,我会这样做。