选择作为相关表的前N个结果的项目

Mik*_*keJ 5 sql subquery limit greatest-n-per-group

假设我有一个问题被问到的游戏,人们发布得分的回复,并且前10个回复获胜.我有一个SQL数据库存储所有这些信息,所以我可能有表,如用户,问题和响应.Responses表有foreign_keys user_id和question_id,以及属性total_score.

显然,对于一个特定问题,我可以通过订单和限制检索前10个响应:

SELECT * FROM Responses WHERE question_id=? ORDER BY total_score DESC LIMIT 10;
Run Code Online (Sandbox Code Playgroud)

我正在寻找的是一种方法,我可以为特定用户确定他们作为获胜者的所有回复的列表(在他们的特定问题的前10名).以编程方式简单地逐步执行每个响应并查看它是否包含在其问题的前10个中,但我想优化它以便我不进行N + 1个查询,其中N是用户提交的响应数量.

Bil*_*win 3

如果您使用 Oracle、Microsoft SQL Server、DB2 或 PostgreSQL,这些数据库支持窗口函数。将用户的回答加入到同一问题的其他回答中。然后按问题分区,按分数降序排列。使用每个分区中的行号将集合限制为前 10 名中的行号。同时传递给定用户的 user_id,以便您可以从前 10 名中挑选他们,因为您只对给定用户的响应感兴趣。

SELECT *
FROM (
  SELECT r1.user_id AS given_user, r2.*,
    ROW_NUMBER() OVER (PARTITION BY r2.question_id ORDER BY r2.total_score DESC) AS rownum
  FROM Responses r1 JOIN Responses r2 ON r1.question_id = r2.question_id
  WHERE r1.user_id = ?
) t
WHERE rownum <= 10 AND user_id = given_user;
Run Code Online (Sandbox Code Playgroud)

但是,如果您使用 MySQL 或 SQLite 或其他不支持窗口函数的数据库,则可以使用以下不同的解决方案:

查询用户的回答,并使用连接来匹配具有更高分数的其他回答(或者在平局的情况下更早的 PK)。按问题分组,并计算得分较高的回复数。如果计数小于 10,则用户的回答属于每个问题的前 10 名。

SELECT r1.*
FROM Responses r1
LEFT OUTER JOIN Responses r2 ON r1.question_id = r2.question_id 
  AND (r1.total_score < r2.total_score 
    OR r1.total_score = r2.total_score AND r1.response_id > r2.response_id)
WHERE r1.user_id = ?
GROUP BY r1.question_id
HAVING COUNT(*) < 10;
Run Code Online (Sandbox Code Playgroud)