使用 FORCE INDEX 确保在计算用户变量之前使用 GROUP BY 和 ORDER BY 对表进行排序
Posted
技术标签:
【中文标题】使用 FORCE INDEX 确保在计算用户变量之前使用 GROUP BY 和 ORDER BY 对表进行排序【英文标题】:using FORCE INDEX to ensure the table is ordered with GROUP BY and ORDER BY before calculating user variables 【发布时间】:2021-04-07 06:20:49 【问题描述】:我正在尝试对第 n 个最高的行求和。
我正在计算一个自行车联赛表,其中第 1 快的车手在某项赛事中获得 50 分,第 2 快的车手获得 49 分,依此类推....联赛中有 10 项赛事,但仅使用车手的 8 项最佳成绩(这意味着一个骑手最多可以错过 2 场比赛而没有在排行榜上出现灾难性的体面) 首先,我需要一个表格,将联盟中所有赛事的每个车手的成绩分组在一起,并按最高分的顺序列出,然后计算一个序号,以便我可以总结 8 个或更少的最佳成绩。 所以我用这个表选择:
set @r := 0, @rn := 0 ;
SELECT
t.*,
@rn := if(@r = t.id_rider, @rn + 1, 1) as seqnum,
@r := t.id_rider as dummy_rider
from results as t
ORDER BY t.id_rider, t.points desc
表results
是如下视图:
SELECT
a.id_rider,
b.id_event,
b.race_no,
b.id_race,
b.id_race_type,
b.`position`,
c.id_league,
(51 - b.`position`) AS points
FROM
wp_dtk_start_sheet a
JOIN wp_dtk_position_results b ON a.id_event = b.id_event AND a.race_no = b.race_no
JOIN wp_dtk_league_races c ON b.id_race = c.id_race
WHERE
c.id_league = 1
AND b.`position` IS NOT NULL
这不起作用,因为 seqnum
对于所有结果都是 1。如果我将视图表导出到 excel 并使用相同的列和数据创建一个测试表,它可以正常工作。我相信问题出在表在运行变量之前没有按ORDER BY t.id_rider, t.points desc
排序
此参考:https://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/ 声明“这种技术几乎是不确定的,因为它依赖于您和我无法直接控制的事物,例如 mysql 决定使用哪些索引进行分组”
此参考建议尝试强制索引使用id_rider
,所以我尝试了:
set @r := 0, @rn := 0 ;
SELECT
a.id_rider,
c.id_league,
(51- b.`position`) as points,
@rn := if(@r = a.id_rider, @rn + 1, 1) as seqnum,
@r := a.id_rider as 'set r'
from wp_dtk_start_sheet as a force index (id_rider)
join wp_dtk_position_results as b on a.id_event = b.id_event and a.race_no = b.race_no
join wp_dtk_league_races as c on b.id_race = c.id_race
where c.id_league = 1 and b.`position` is not null
ORDER BY a.id_rider, points desc
这不起作用,我像以前一样得到了所有行的 seqnum =1
我的表结构如下:
表 a - wp_dtk_start_sheet
表 b - wp_dtk_position_results
表 c -wp_dtk_league_races
这个堆栈过低的答案也很有帮助,但也有同样的问题: Sum Top 10 Values
谁能帮忙?也许我的做法完全错了?
【问题讨论】:
【参考方案1】:Bill 的回答非常出色,但我也将其合并为一个语句,这是合并的 select 命令:
Select
t.id_rider,
sum(points) as total
from
(SELECT
a.id_rider,
c.id_league,
(51- b.`position`) as points,
ROW_NUMBER() OVER (PARTITION BY id_rider ORDER BY points DESC) AS seqnum
from wp_dtk_start_sheet as a
join wp_dtk_position_results as b on a.id_event = b.id_event and a.race_no = b.race_no
join wp_dtk_league_races as c on b.id_race = c.id_race
where c.id_league = 1 and b.`position` is not null ) as t
where seqnum <= 8
group by id_rider
order by total desc
【讨论】:
【参考方案2】:如果您使用window functions,解决方案会更加清晰。这允许您指定每个组中的行顺序以用于行编号。
SELECT t.*
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY id_rider ORDER BY points DESC) AS seqnum
FROM results
) AS t
WHERE t.seqnum <= 8;
在 MySQL 8.0 中引入了对窗口函数的支持,因此您可能需要升级。但它自 2018 年以来一直是 MySQL 产品的一部分。
【讨论】:
以上是关于使用 FORCE INDEX 确保在计算用户变量之前使用 GROUP BY 和 ORDER BY 对表进行排序的主要内容,如果未能解决你的问题,请参考以下文章
在 mysql 查询中使用 force index 子句可能有啥缺点? [关闭]
Mysql force index和ignore index 使用实例
业务安全-03业务逻辑漏洞之暴力破解(Burte Force)