这是我在开发排名系统时遇到的一个有趣问题。
三张桌子:
目标是生成一个用户列表,按照他们收到的前10个日志的点数进行排序。对于每个用户,我必须找到他/她的前10个日志(按点排序),然后按此编号对所有用户进行排序。
我还必须能够限制其他参数考虑的路线和用户 - >用户年龄,路线难度,日志日期。例如,在25岁以上添加难度为1-8的路径的所有用户中创建此排名列表,这些路径不超过一个月(日志)。
到目前为止,这是我所得到的: 1.我知道如何根据总点数对用户进行选择和排序:
SELECT
l.id, l.idr,
u.name,..., ...,
SUM(l.points) as totalPoints
FROM logs l
INNER JOIN routes r ON l.idr = r.id
INNER JOIN users u ON d.idu = u.id
WHERE
/*
All the conditions I need
*/
GROUP BY u.id
ORDER BY totalPoints DESC
为一个用户选择前10名:
SELECT SUM(points)
FROM (
SELECT l.points as points
FROM logs l
INNER JOIN users u ON l.idu = u.id
WHERE u.id = '1'
LIMIT 10
) AS T
我只是不知道如何有效地将它们放在一起。我已经通过一些TEMPORARY TABLES解决了这个问题,并在PHP中使用while循环请求其他数据,但这非常非常慢且效率低下。随着数据库越来越大(日志~40 000,用户〜2 000,路由~1 000条记录),需要更有效的解决方案。
正如我所提到的,我正在使用PHP,所以如果你有一些想法如何更快(更高效),不是通过创建一个很棒的查询,而是通过几个更聪明的小查询和PHP中的一些,这将是真的也是如此。
感谢您的任何想法:)
答案 0 :(得分:1)
考虑一个相关计数子查询来计算排名列,然后您可以将其用作外部查询中的过滤器。如果MySQL支持窗口函数,通常的方法是RANK OVER()
CTE解决方案。
以下是用户的两个示例,然后是用户和路由。请注意,对于每个分组级别,您添加WHERE
子句以对GROUP BY
派生表中的子查询和t
列进行排名。每个特殊WHERE
条件也是如此,因为它们必须在子查询中进行镜像。
用户 (每位用户的前十大日志)
SELECT main.`user`, main.totalPoints
FROM
(SELECT t.name as `user`, SUM(t.points) as totalPoints
FROM
(SELECT l.id, l.idr, u.name, l.points,
(SELECT Count(*) FROM logs sub
WHERE sub.idu = l.idu
AND sub.points >= l.points) AS user_rank
FROM logs l
INNER JOIN users u ON l.idu = u.id) AS t
WHERE t.user_rank <= 10
GROUP BY t.name) AS main
ORDER BY main.totalPoints
用户和路线 (每个用户的每条路线前10个日志)
SELECT main.`user`, main.route, main.totalPoints
FROM
(SELECT t.name as `user`, t.idr as route, SUM(t.points) as totalPoints
FROM
(SELECT l.id, l.idr, u.name, l.points,
(SELECT Count(*) FROM logs sub
WHERE sub.idu = l.idu
AND sub.idr = l.idr
AND sub.points >= l.points) AS user_route_rank
FROM logs l
INNER JOIN routes r ON l.idr = r.id
INNER JOIN users u ON l.idu = u.id) AS t
WHERE t.user_route_rank <= 10
GROUP BY t.name, t.idr) AS main
ORDER BY main.totalPoints
注意:
TIES :此方法中包含ARE的记录点。有一些方法可以使用最低级的主键,如下所示:
(SELECT Count(*) FROM logs sub
WHERE sub.idu = l.idu
AND sub.idr = l.idr
AND (sub.points >= l.points
OR sub.points = l.points AND sub.id <= l.id)) AS user_route_rank
GROUP BY :您的上述不完整GROUP BY
聚合已被删除。 MySQL允许它,但在其他所有RDMS中都会失败。如果你有ANSI
or ONLY FULL GROUP BY
mode,MySQL就会引发错误,因为聚合查询的SELECT
子句中的非聚合列也必须包含在GROUP BY
中(尽管反向有效) ANSI)。