(mysql)选择50个评分最高的项目,每个用户最多一个项目

时间:2014-03-04 19:05:15

标签: mysql greatest-n-per-group

我不确定如何在MySQL中有效地执行此操作并感谢任何帮助。

目标是选择50个畅销商品,每个用户最多一件商品。我习惯用CTE或DISTINCT ON做这个,但当然这不是MySQL中的一个选项。我希望有一个单一查询解决方案,我想避免使用存储过程。

基本架构是用户发布的项目表,以及带有确定特定销售得分的字段的销售表。

CREATE TABLE items (
item_id INT PRIMARY KEY,
user_id INT NOT NULL
)
CREATE TABLE sales (
item_id INT NOT NULL,
score INT NOT NULL
)
-- Create some sample data
INSERT INTO items VALUES (1, 1), (2, 1), (3, 1), (4, 2), (5, 2), (6, 3), (7, 3);
INSERT INTO sales VALUES (1, 1), (1, 1), (2, 1), (3, 2), (3, 1), (4, 3), (4, 2), (5, 2), (6, 1), (6, 1), (6, 1), (7, 2);

对此示例数据的查询结果应为

+---------+---------+-------------+
| user_id | item_id | total_score |
+---------+---------+-------------+
|       2 |       4 |           5 |
|       1 |       3 |           3 |
|       3 |       6 |           3 |
+---------+---------+-------------+

这是PostgreSQL解决方案:

SELECT DISTIN ON (items.user_id)
    items.user_id,
    items.item_id,
    SUM(sales.score) AS total_score
FROM items
JOIN sales ON (sales.item_id = items.item_id)
GROUP BY items.item_id
ORDER BY total_score DESC
LIMIT 50

这是我提出的MySQL解决方案,但它非常难看。我尝试使用临时表基本上做同样的事情,但在这个过程中我知道MySQL不允许在同一个查询中多次加入临时表。

SELECT items_scores.user_id, items_scores.item_id, items_scores.total_score
FROM (
    SELECT items.user_id, items.item_id, SUM(sales.score) as total_score
    FROM items
    JOIN sales ON
        sales.item_id = items.item_id
    GROUP BY items.item_id
    ) AS items_scores
WHERE items_scores.total_score =
    (
    SELECT MAX(t.total_score)
    FROM (
        SELECT items.user_id, items.item_id, SUM(sales.score) as total_score
        FROM items
        JOIN sales ON
            sales.item_id = items.item_id
        GROUP BY items.item_id
        ) AS t
    WHERE t.user_id = items_scores.user_id
    )
ORDER BY items_scores.total_score DESC

1 个答案:

答案 0 :(得分:0)

MySQL查询:

select user, item, total_score 
from (
     select sum(sales.score) as total_score, items.user_id as user, items.item_id as item
     from sales 
     inner join items on sales.item_id = items.item_id 
     group by item,user 
     order by total_score desc) as t
 group by user limit 50;

Output:

+------+------+-------------+
| user | item | total_score |
+------+------+-------------+
|    1 |    3 |           3 |
|    2 |    4 |           5 |
|    3 |    6 |           3 |
+------+------+-------------+
3 rows in set (0.00 sec)

一些解释

MySQL documentation说:

但是,当GROUP BY中未命名的每个非聚合列中的所有值对于每个组都相同时,这非常有用。服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的。此外,添加ORDER BY子句不会影响每个组中值的选择。选择值后会对结果集进行排序,而ORDER BY不会影响服务器选择的每个组中的值。

在我们的子查询中......非聚合列是user_id和item_id,我们希望它们对于我们正在进行求和的每个组都是相同的。此外,我们没有做任何可以影响聚合的订单。我们希望总结该组的所有价值。最后,我们将输出排序并将其保存为派生表。

最后,我们在这个派生表上运行一个select查询,我们在那里执行Group By user ..并将输出限制为50