我有两张 table 。
一个包含产品列表,主键是产品 ID。假设大约 10 列产品信息已被压缩为一列。
另一个包含用户对产品给出的评分列表。这些列是产品 ID、用户 ID 和评分。此表中的条目可能比产品表多一个数量级。
我想在单个查询中获取产品的所有信息,以及它的平均用户评分和用户评分数量。
这似乎是一种正确的方法:
SELECT
p.p_id,
p.product_info,
( SELECT AVG(score) FROM ratings AS r WHERE r.p_id = p.p_id ) avg_rating,
( SELECT COUNT(score) FROM ratings AS r WHERE r.p_id = p.p_id ) num_ratings
FROM products AS p
真正的问题:随着我的数据库扩展,从性能的角度来看这看起来如何?这可以使用更少的子查询并用连接替换它们吗?
附带问题:我曾经有一个计划,我会在产品表中缓存每个产品的平均评分和评分数量,并在新分数或更新分数到达时更新它。这使得查询非常简单,但我的直觉告诉我这真的很天真。假设这是一个 InnoDB 表,有人可以更明确地解释为什么这种缓存可能是也可能不是一个好主意吗?
最佳答案
如果 product_info
是一个相当长的 VARCHAR,下面的查询可能会更快(假设您有一个关于 ratings
和 p_id 的复合索引 (p_id, score)
在 products
中编入索引):
SELECT
p_id,
product_info,
avg_rating,
num_ratings
FROM (
SELECT p_id, AVG(score) as avg_rating, COUNT(score) as num_ratings
FROM ratings
GROUP BY p_id
) as aggr
JOIN products USING (p_id);
连接的顺序反射(reflect)了 MySQL 更愿意执行查询的顺序(因为子查询的结果没有索引)。
但是当 ratings
至少包含每个产品的单个记录时,查询效果很好,否则您将需要为其余的添加一个带零的 UNION ALL
产品(这可能会使速度明显变慢)。
当第一个查询不够快时,使用预先计算的聚合的解决方案成为一个好主意。
关于带有数学运算的 MySQL 子查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9016632/