如何用单条SQL查询获取排行榜中用户及前后相邻排名用户?
用单条SQL获取排行榜中当前用户的相邻用户
当然可以用单条SQL搞定这个需求!不用分开跑两次查询,咱们有两种靠谱的方法实现,看完你就懂了~
方法一:用UNION ALL合并三个查询
这个方法和你现在用的两条查询逻辑很接近,只是把当前用户的查询也加进去,用UNION ALL合并成一条语句,一次性拿到三个用户的数据:
-- 先取当前用户的数据 SELECT * FROM users WHERE id = [USER.ID] UNION ALL -- 取积分更高(或同积分)的相邻用户:找比当前用户积分高的最小值,同积分则取id相邻的 SELECT TOP 1 * FROM users WHERE (points > [USER.POINTS]) OR (points = [USER.POINTS] AND id != [USER.ID]) ORDER BY points ASC, id ASC UNION ALL -- 取积分更低(或同积分)的相邻用户:找比当前用户积分低的最大值,同积分则取id相邻的 SELECT TOP 1 * FROM users WHERE (points < [USER.POINTS]) OR (points = [USER.POINTS] AND id != [USER.ID]) ORDER BY points DESC, id DESC
我稍微调整了条件,主要是处理同积分用户的情况——如果只写points >=,可能会拿到一堆同积分的用户,加上OR (points = [USER.POINTS] AND id != [USER.ID])再按id排序,能确保取到真正相邻的同积分用户,避免逻辑漏洞。
方法二:用窗口函数(更优雅,推荐)
如果你的数据库支持窗口函数(比如SQL Server 2012+、MySQL 8+、PostgreSQL这些主流数据库都支持),用LAG()和LEAD()函数能写出更简洁、易维护的SQL:
WITH ranked_users AS ( SELECT *, -- 拿到当前用户的「上一位」(积分更高/同积分的前一个用户ID) LAG(id) OVER (ORDER BY points DESC, id ASC) AS prev_user_id, -- 拿到当前用户的「下一位」(积分更低/同积分的后一个用户ID) LEAD(id) OVER (ORDER BY points DESC, id ASC) AS next_user_id FROM users ) -- 关联users表,取出当前用户、上一位、下一位的完整数据 SELECT u.* FROM ranked_users r JOIN users u ON u.id IN (r.id, r.prev_user_id, r.next_user_id) WHERE r.id = [USER.ID]
这个思路是先给所有用户按积分降序、id升序排好序(积分相同的用户按id稳定排序),然后用LAG()获取当前用户的前一个用户ID,LEAD()获取后一个用户ID,最后通过关联查询把这三个用户的数据捞出来。
小提醒
- 如果当前用户是排行榜第一名,
prev_user_id会是NULL,结果里只会有当前用户和下一位;同理如果是最后一名,next_user_id是NULL,结果里只有当前用户和上一位。 - 排序规则里加
id ASC是为了让同积分用户有固定的排名顺序,避免相邻用户随机变化。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

