PostgreSQL选择时间段内的最高值
使用Rails,我试图执行一个SQL命令来返回一个包含某个用户每天最高值的行数组 例如:PostgreSQL选择时间段内的最高值,sql,ruby-on-rails,postgresql,greatest-n-per-group,Sql,Ruby On Rails,Postgresql,Greatest N Per Group,使用Rails,我试图执行一个SQL命令来返回一个包含某个用户每天最高值的行数组 例如: user_id(integer) | created_at(datetime) | score(integer) -------------------+--------------------------------+--------------- 1 | "2015-07-27 21:35:24" | 100
user_id(integer) | created_at(datetime) | score(integer)
-------------------+--------------------------------+---------------
1 | "2015-07-27 21:35:24" | 100
1 | "2015-07-27 21:35:24" | 123
2 | "2015-07-27 21:35:24" | 101
2 | "2015-07-27 21:35:24" | 122
3 | "2015-07-27 21:35:24" | 103
3 | "2015-07-27 21:35:24" | 115
1 | "2015-07-26 21:35:24" | 116
1 | "2015-07-26 21:35:24" | 151
2 | "2015-07-26 21:35:24" | 122
2 | "2015-07-26 21:35:24" | 134
3 | "2015-07-26 21:35:24" | 123
3 | "2015-07-26 21:35:24" | 111
1 | "2015-07-25 21:35:24" | 129
1 | "2015-07-25 21:35:24" | 152
2 | "2015-07-25 21:35:24" | 120
2 | "2015-07-25 21:35:24" | 109
3 | "2015-07-25 21:35:24" | 142
3 | "2015-07-25 21:35:24" | 131
预期成果:
user_id(integer) | created_at(datetime) | score(integer)
-------------------+--------------------------------+---------------
1 | "2015-07-27 21:35:24" | 123
2 | "2015-07-27 21:35:24" | 122
3 | "2015-07-27 21:35:24" | 115
1 | "2015-07-26 21:35:24" | 151
2 | "2015-07-26 21:35:24" | 134
3 | "2015-07-26 21:35:24" | 123
1 | "2015-07-25 21:35:24" | 152
2 | "2015-07-25 21:35:24" | 120
3 | "2015-07-25 21:35:24" | 142
我一直在组合各种连接,拥有
和其他方法,但都没有用。我无法让它过滤结果。通过select
ing每日最大值,我取得了一些进步,但是我无法根据每个user\u id
筛选出较低的值。我在Rails中使用了multiplegroupby
和map
实现了这一点,但速度非常慢,因为它必须在整个数组中重新迭代,而且有很多记录,这可能需要一些时间
编辑:
我的解决办法如下:
all_scores_in_time_period = UserScore
.where("EXTRACT(MONTH FROM created_at) = ?", Date::MONTHNAMES.index(params[:month_control]))
.where("EXTRACT(YEAR FROM created_at) = ?", params[:year_control])
.select("DISTINCT ON(DATE(created_at), user_id) *")
.order("DATE(created_at) desc")
这首先按月/年进行过滤,然后按每个用户每天的最高分数返回用户列表。您可以使用
distinct on()
,这是distinct
操作符的Postgres扩展:
select distinct on (user_id, created_at) user_id, created_at, score
from the_table
order by user_id, created_at, score desc;
如果您希望使用标准SQL解决方案,也可以使用窗口函数实现:
select user_id, created_at, score
from (
select user_id, created_at, score,
row_number() over (partition by user_id, created_at order by score desc) as rn
from the_table
) as t
order by user_id, created_at;
使用distinct on()
的解决方案在Postgres中通常更快
通过一个窗口功能,你还可以处理关系:当一个用户在一天中多次获得相同(最高)分数时。带有row\u number()
的解决方案将仅为每个(用户id,创建时间)返回一行。如果希望所有行具有相同(最高)分数,则需要使用densite\u rank()
编辑
如果要忽略时间戳列的时间部分,只需将其强制转换为日期:
created_at::date
“21:35:24”对于所有阻止对解决方案进行彻底测试的日子(因为它有效地将时间戳转换为最新时间),请提供更多真实的时间戳。@Dzenly-时间不是必需的。无论如何,它都要按日期过滤,而不是按日期时间过滤。我写了《泰晤士报》,试图更明确一些,但你是对的——它确实有点混淆了要点。
distinct on
是我一直在寻找的。非常感谢。