使用Rails,我正在尝试执行SQL命令以返回每天包含特定用户最高值的行数组。
例如:
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
我一直在组合各种联接,having
和其他方法,但无济于事。我无法过滤结果。我通过select
每天的最大值取得了一点进展,但之后我无法按user_id
基础过滤掉较低的值。我设法在Rails中使用多个group_by
和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")
首先按月/年过滤,然后按用户每天的最高得分返回用户列表。
答案 0 :(得分:3)
您可以使用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;
Postgres中distinct on()
的解决方案通常更快。
使用窗口功能,您还可以处理关系:当用户在一天中具有相同(最高)得分时不止一次。 row_number()
的解决方案每个只返回一行(user_id,created_at)。如果您希望所有行具有相同(最高)分数,则需要使用dense_rank()
代替。
修改
如果要忽略timestamp列的时间部分,只需将其强制转换为日期:
created_at::date