我如何找到每年排名前 N 的击球手?
Posted
技术标签:
【中文标题】我如何找到每年排名前 N 的击球手?【英文标题】:How do I find the top N batters per year? 【发布时间】:2010-06-17 16:47:47 【问题描述】:我在 mysql 实例中使用 Lahman Baseball Database。我想找到每年都在本垒打(HR)中名列前茅的球员。 Batting 表的架构具有以下(相关部分):
+-----------+----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------------------+------+-----+---------+-------+
| playerID | varchar(9) | NO | PRI | | |
| yearID | smallint(4) unsigned | NO | PRI | 0 | |
| HR | smallint(3) unsigned | YES | | NULL | |
+-----------+----------------------+------+-----+---------+-------+
每一年,每个玩家都有一个条目(每年数百到 12k,可追溯到 1871 年)。一年内获得前 N 名击球手很容易:
SELECT playerID,yearID,HR
FROM Batting
WHERE yearID=2009
ORDER BY HR DESC LIMIT 3;
+-----------+--------+------+
| playerID | yearID | HR |
+-----------+--------+------+
| pujolal01 | 2009 | 47 |
| fieldpr01 | 2009 | 46 |
| howarry01 | 2009 | 45 |
+-----------+--------+------+
但我有兴趣从每年中找出前 3 名。我找到了像 this 这样的解决方案,描述了如何从一个类别中选择顶部,我试图将它应用到我的问题中,结果却得到一个永远不会返回的查询:
SELECT
b.yearID, b.playerID, b.HR
FROM
Batting AS b
LEFT JOIN
Batting b2
ON
(b.yearID=b2.yearID AND b.HR <= b2.HR)
GROUP BY b.yearID HAVING COUNT(*) <= 3;
我哪里出错了?
【问题讨论】:
您需要先按PED使用情况(降序)排序。 【参考方案1】:这样的事情应该工作:
SELECT b.playerID, b.yearID, b.HR
FROM Batting b
WHERE HR >= (
SELECT b2.HR
FROM Batting b2
WHERE b2.yearID=b1.yearID
ORDER BY b2.HR DESC
LIMIT 2, 1
)
ORDER BY b.yearID DESC, b.HR DESC;
解释:选择所有本垒打数 >= 数为当年第三高的行。这不会断绝关系。因此,如果有多个击球手拥有相同数量的本垒打,他们都会出现。
结果是从最近一年开始排序的,每年按排名进行子排序。
注意:LIMIT 是从 0 开始的偏移量,所以 2, 1 表示从第二行开始抓一行,即:第三行。
【讨论】:
LIMIT 解释加 1。 LIMIT 参数实际上是相反的:偏移量,然后是行数。此外,您在子查询中有一个错误——b1
应该只是 b
。除此之外,这是正确的。自 2005 年以来,just 花了 4:18 才找到结果(Macbook Pro、OS X 10.6.3、Core 2 2.5GHz、足够的 RAM 可以将所有数据存储在内存中),所以也许有一些进行优化。
感谢您指出限制细节。我编辑了我的答案。您是否将查询缓存设置为足够大以将所有内容保存在内存中?它如此缓慢的原因是 Batting 中的每一行都执行了非常昂贵的子查询。可能的优化是在 yearID 上添加一个索引并在 HR 上添加另一个索引。进一步的优化是制作一个临时表,该表每年保存第三高的 HR 计数以进行比较。
太棒了,感谢您的提示!索引 yearID 和 HR 在 2 秒内完成了查询。【参考方案2】:
哇,随机。我碰巧在 Lahman Baseball DB 上使用article 模拟 Oracle 分析函数进行了相同的查询(尽管是工资)。这个版本的查询很简洁,但不是那么直观。
select *
from (
select
b.yearID as year,
b.teamID as team,
m.nameFirst as first,
m.nameLast as last,
find_in_set(b.HR, x.teamRank) as rank,
b.HR as HR
from
Batting b
inner join Master m on m.playerID = b.playerID
inner join (select yearID, group_concat(distinct HR order by HR desc) as teamRank from Batting group by yearID) x on x.yearID = b.yearID
) x
where
rank <= 10 and rank > 0
order by
year desc, rank
或者 2010 年每个团队的前 5 名人力资源总数......
select *
from (
select
b.yearID as year,
b.teamID as team,
m.nameFirst as first,
m.nameLast as last,
b.HR as HR,
find_in_set(b.HR, x.teamRank) as rank
from
Batting b
inner join Master m on m.playerID = b.playerID
inner join (select teamID, group_concat(distinct HR order by HR desc) as teamRank from Batting where yearID = 2010 group by teamID) x on x.teamID = b.teamID
where
b.yearID = 2010
) x
where
rank <= 5 and rank > 0
order by
team, rank
limit 12
显示这些结果...
year team first last HR rank
2010 ARI Mark Reynolds 32 1
2010 ARI Chris Young 27 2
2010 ARI Kelly Johnson 26 3
2010 ARI Adam LaRoche 25 4
2010 ARI Justin Upton 17 5
2010 ATL Brian McCann 21 1
2010 ATL Jason Heyward 18 2
2010 ATL Troy Glaus 16 3
2010 ATL Martin Prado 15 4
2010 ATL Eric Hinske 11 5
2010 BAL Luke Scott 27 1
2010 BAL Ty Wigginton 22 2
【讨论】:
以上是关于我如何找到每年排名前 N 的击球手?的主要内容,如果未能解决你的问题,请参考以下文章