如何有效地计算大表的平均值?

时间:2017-03-06 09:32:53

标签: php mysql sql

我有一个名为 rating 的表格,其中包含以下字段:

+-----------+------------+------+-----+---------+----------------+
| Field     | Type       | Null | Key | Default | Extra          |
+-----------+------------+------+-----+---------+----------------+
| rating_id | bigint(20) | NO   | PRI | NULL    | auto_increment |
| user_id   | int(11)    | NO   | MUL | NULL    |                |
| movie_id  | int(11)    | NO   |     | NULL    |                |
| rating    | float      | NO   |     | NULL    |                |
+-----------+------------+------+-----+---------+----------------+

此表中的索引:

+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| ratings |          0 | PRIMARY  |            1 | rating_id   | A         |      100076 |     NULL | NULL   |      | BTREE      |         |               |
| ratings |          0 | user_id  |            1 | user_id     | A         |         564 |     NULL | NULL   |      | BTREE      |         |               |
| ratings |          0 | user_id  |            2 | movie_id    | A         |      100092 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

我有另一个名为movie_average_ratings的表格,其中包含以下字段:

+----------------+---------+------+-----+---------+-------+
| Field          | Type    | Null | Key | Default | Extra |
+----------------+---------+------+-----+---------+-------+
| movie_id       | int(11) | NO   | PRI | NULL    |       |
| average_rating | float   | NO   |     | NULL    |       |
+----------------+---------+------+-----+---------+-------+

因为很明显,我想从评分表中计算电影的平均评分,并更新movie_average_ratings表格。我尝试了以下SQL查询。

UPDATE movie_average_ratings
SET average_rating = (SELECT AVG(rating)
                            FROM ratings
                            WHERE ratings.movie_id = movie_average_ratings.movie_id);

目前,大约有10,000条电影记录和100,000条评级记录,我收到Lock wait timeout exceeded; try restarting transaction错误。记录数量可能会显着增长,因此我认为增加超时不是一个好的解决方案。

那么,我如何编写'可伸缩'查询来实现这一目标呢?迭代movie_average_ratings表记录并单独计算平均值是最有效的解决方案吗?

3 个答案:

答案 0 :(得分:1)

如果没有explain,就很难清楚是什么阻碍了你。通过将此聚合数据存储为非规范化表,您还不清楚是否会获得性能提升 - 如果计算评级的查询在0.04秒内执行,则不太可能查询非规范化表格会更快。

一般情况下,如果您知道您遇到了性能问题,我建议仅进行非规范化。

但这不是问题。

我会做以下事情:

delete from movie_average_ratings;

insert into movie_average_ratings
Select movie_ID, avg(rating) 
from ratings 
group by movie_id;

答案 1 :(得分:0)

我刚在另一个post找到了一些东西:

  

发生的事情是,其他一些线程正在进行记录锁定   一些记录(你正在更新表中的每条记录!)太久了,   并且你的线程正在超时。

这意味着您的某些记录已被锁定,您可以在控制台中强制解锁它们:

  

1)输入MySQL @path\script.sql

     

2)让我们看一下锁定表mysql -u your_user -p

的列表      

3)让我们看一下当前进程的列表,其中一个是锁定的   你的桌子mysql> show open tables where in_use>0;

     

4)杀死其中一个进程mysql> show processlist;

答案 2 :(得分:0)

您可以将movie_average_ratings表重新设计为

movie_id (int)
sum_of_ratings (int)
num_of_ratings (int)

然后,如果添加了新评级,您可以将其添加到movie_average_ratings并计算平均值(如果需要)