我有一个评级表,每个用户每天可以添加一个评级。但是每个用户可能会在评分之间错过几天。
我希望得到rating
的每个user_id
前7个条目的平均created_at
。
我的表:
mysql> desc entries;
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| rating | tinyint(4) | NO | | NULL | |
| user_id | int(10) unsigned | NO | MUL | NULL | |
| created_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
理想情况下我会得到类似的东西:
+------------+------------------+
| day | average_rating |
+------------+------------------+
| 1 | 2.53 |
+------------+------------------+
| 2 | 4.30 |
+------------+------------------+
| 3 | 3.67 |
+------------+------------------+
| 4 | 5.50 |
+------------+------------------+
| 5 | 7.23 |
+------------+------------------+
| 6 | 6.98 |
+------------+------------------+
| 7 | 7.22 |
+------------+------------------+
我能得到的最接近的是:
SELECT rating, user_id, created_at FROM entries ORDER BY user_id asc, created at desc
这根本不是很接近......
甚至可能吗?表演会不会很糟糕?每次加载网页时都需要运行一些东西,那么每天运行一次并保存结果会更好吗? (到另一张桌子!?)
编辑 - 第二次尝试
努力寻求解决方案,我认为这会得到每个用户第一天的评分:
select rating from entries where user_id in
(select user_id from entries order by created_at limit 1);
但我明白了:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
所以现在我要和JOIN
一起玩,看看是否有帮助。
编辑 - 第三次尝试,越来越近
I found this stackoverflow post,更接近我想要的。
select e1.* from entries e1 left join entries e2
on (e1.user_id = e2.user_id and e1.created_at > e2.created_at)
where e2.id is null;
它获得每个用户第一天的评分。
下一步是弄清楚如何获得第2天到第7天。我不能使用1.created_at > e2.created_at
,所以我现在真的很困惑。
编辑 - 第四次尝试
好的,我认为这是不可能的。一旦我弄清楚如何关闭&#39;完整的群组。模式,我意识到我可能需要使用limit <user_id>, <day_num>
的子查询,我得到了:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
我目前的方法是获取整个表格,并使用PHP计算每天的平均值。
答案 0 :(得分:1)
如果我理解正确,您希望获取用户提供的最后7个评分,并按照评分日期排序。一个用户的最后7个评级可能会在不同的日期落到另一个用户,但无论日期如何,它们都将被平均在一起。
首先,我们需要按用户和日期对数据进行排序,并为每个用户提供自己的递增行数。我这样做是通过添加两个变量,一个用于最后一个用户ID,另一个用于行号:
select e.created_at,
e.rating,
if(@lastUser=user_id,@row := @row+1, @row:=1) as row,
@lastUser:= e.user_id as user_id
from entries e,
( select @row := 0, @lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc;
如果前一个user_id
不同,我们将行计数器重置为1.结果是:
+---------------------+--------+------+---------+
| created_at | rating | row | user_id |
+---------------------+--------+------+---------+
| 2017-01-10 00:00:00 | 1 | 1 | 1 |
| 2017-01-09 00:00:00 | 1 | 2 | 1 |
| 2017-01-08 00:00:00 | 1 | 3 | 1 |
| 2017-01-07 00:00:00 | 1 | 4 | 1 |
| 2017-01-06 00:00:00 | 1 | 5 | 1 |
| 2017-01-05 00:00:00 | 1 | 6 | 1 |
| 2017-01-04 00:00:00 | 1 | 7 | 1 |
| 2017-01-03 00:00:00 | 1 | 8 | 1 |
| 2017-01-02 00:00:00 | 1 | 9 | 1 |
| 2017-01-01 00:00:00 | 1 | 10 | 1 |
| 2017-01-13 00:00:00 | 1 | 1 | 2 |
| 2017-01-11 00:00:00 | 1 | 2 | 2 |
| 2017-01-09 00:00:00 | 1 | 3 | 2 |
| 2017-01-07 00:00:00 | 1 | 4 | 2 |
| 2017-01-05 00:00:00 | 1 | 5 | 2 |
| 2017-01-03 00:00:00 | 1 | 6 | 2 |
| 2017-01-01 00:00:00 | 1 | 7 | 2 |
| 2017-01-13 00:00:00 | 1 | 1 | 3 |
| 2017-01-01 00:00:00 | 1 | 2 | 3 |
| 2017-01-03 00:00:00 | 1 | 1 | 4 |
| 2017-01-01 00:00:00 | 1 | 2 | 4 |
| 2017-01-02 00:00:00 | 1 | 1 | 5 |
+---------------------+--------+------+---------+
我们现在只需将其包装在另一个语句中,以选择行号小于或等于7的平均值。
select e1.row day, avg(e1.rating) avg
from (
select e.created_at,
e.rating,
if(@lastUser=user_id,@row := @row+1, @row:=1) as row,
@lastUser:= e.user_id as user_id
from entries e,
( select @row := 0, @lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc) e1
where e1.row <=7
group by e1.row;
输出:
+------+--------+
| day | avg |
+------+--------+
| 1 | 1.0000 |
| 2 | 1.0000 |
| 3 | 1.0000 |
| 4 | 1.0000 |
| 5 | 1.0000 |
| 6 | 1.0000 |
| 7 | 1.0000 |
+------+--------+