2017-02-14 77 views
0

我有一個評分表,其中每個用戶每天可以添加一個評級。但每個用戶可能會錯過評級之間的幾天。MySQL - 爲B列的前N個條目選擇列A的平均值

我想要得到平均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 

其中ISN一點都不太接近...

它甚至有可能嗎?表演會很糟糕嗎?這是每次加載網頁時都需要運行的東西,那麼每天只運行一次並保存結果會更好嗎? (另一個表!?)

編輯 - 第二次嘗試

爭取解決工作,我認爲這將得到的評價每個用戶的第一天:

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,所以我現在非常困惑。

編輯 - 第四次嘗試

好吧,我認爲這是不可能的。有一次,我摸索出如何「通過整組」模式,關閉,我意識到我可能會需要使用子查詢與limit <user_id>, <day_num>,爲此,我得到:

ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' 

我目前的方法是隻得到了整個表,並使用PHP來計算每天的平均值。

+2

你會在這裏需要行號,以及日曆表中缺少的天,以填補一個給定的用戶,他們應該發生。 –

+0

好的,謝謝 - 你是說在表中添加一個day_number列?不知道你的日曆表是​​什麼意思。如果user1在1月1日和次年1月2日第一次發佈,而user2在3月1日和8月1日之後首先發布,那麼無論發生什麼時候,我都需要所有用戶第一天和第二天的平均評分。 –

+0

缺少日期必須來自_somewhere_,因爲它們不在您的原始表格中。行號也必須來自某個地方。 –

回答

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 | 
+---------------------+--------+------+---------+ 

我們現在簡單地在另一個語句中包裝這個選擇平均行數小於或等於七的平均值。

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 | 
+------+--------+ 
+0

你先生是魔術師!謝謝你一千次,完美的回答,並且非常清楚地解釋。 –

+0

爲了正確工作,我只需做一點細微的改動 - 從底部的第三行開始,你有'e.created_at desc' - 我將它改爲'e.created_at asc' - 但是再次感謝!我仍然如此印象深刻:) –

+0

我不知道你是否需要第一個或最後7天。但是,正如你發現改變排序順序給出了正確的答案。很高興我能幫上忙 – roblovelock