djp*_*r17 1 mysql aggregate-functions
我有一个MySQL表,其中包含运动员进行的一系列测试的数据.我想为每个活动获得最好的结果.
这是包含运动员所有测试数据的表格:
+---------+-----------+-------+
| eventId | athleteId | score |
+---------+-----------+-------+
| 1 | 129907 | 900 |
| 2 | 129907 | 940 |
| 3 | 129907 | 927 |
| 4 | 129907 | 856 |
| 1 | 328992 | 780 |
| 2 | 328992 | 890 |
| 3 | 328992 | 936 |
| 4 | 328992 | 864 |
| 1 | 492561 | 899 |
| 2 | 492561 | 960 |
| 3 | 492561 | 840 |
| 4 | 492561 | 920 |
| 5 | 487422 | 900 |
| 6 | 487422 | 940 |
| 7 | 487422 | 927 |
| 5 | 629876 | 780 |
| 6 | 629876 | 890 |
| 7 | 629876 | 940 |
| 5 | 138688 | 899 |
| 6 | 138688 | 950 |
| 7 | 138688 | 840 |
+---------+-----------+-------+
Run Code Online (Sandbox Code Playgroud)
我需要选择最好的标准阵容,进行最好的测试.我要找的结果应该是:
+---------+-----------+-------+
| eventId | athleteId | score |
+---------+-----------+-------+
| 1 | 129907 | 900 |
| 2 | 492561 | 960 |
| 3 | 328992 | 936 |
| 4 | 492561 | 920 |
| 5 | 487422 | 900 |
| 6 | 138688 | 950 |
| 7 | 629876 | 940 |
+---------+-----------+-------+
Run Code Online (Sandbox Code Playgroud)
如果你想要可靠地获得胜利者(和联合获胜者).以下SQL语句应该这样做......
SELECT athleteId, a.eventId, a.score
FROM tests AS a
JOIN (
-- This select finds the top score for each event
SELECT eventId, MAX(score) AS score
FROM tests
GROUP BY eventId
) AS b
-- Join on the top scores
ON a.eventId = b.eventId
AND a.score = b.score
Run Code Online (Sandbox Code Playgroud)
我正在执行子选择以获得每个事件的最高分数,然后执行内部联接以获得在事件中获得最高分数的各个记录.
我已从评论中的对话汇编了以下信息.
SELECT athleteId, eventId, score
FROM (
SELECT athleteId, eventId, score
FROM tests
ORDER BY eventId, score DESC
) AS a
GROUP BY eventId
Run Code Online (Sandbox Code Playgroud)
我们正在根据事件和分数订购的记录集创建一个组.然后,我们使用分组从列中选择值,以便为每个事件选择一个记录.
如果您使用的是GROUP BY条款,则不再是单个记录,而是一组无序记录!
您可以使用聚合函数在MySQL http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html中执行一些非常强大且有用的交叉记录计算,但是为了关联这些组回到您可能需要执行的个人记录JOIN.
在第二个例子中,我们将组返回,就好像它们是单独的记录一样.
不是在SQL语言中非聚合列是非法的,在MySQL中它们是被允许的,虽然我不能说为什么,它可能是出于性能原因在非规范化列中或者由于某种原因你确定的值为组中的列不会更改.
MySQL为组中的非聚合列选择最简单的返回值.碰巧在记录集分组之前选择它遇到的第一个值,但是,它不一定会一直这样做!
MySQL文档声明包含a的select中非聚合列的值GROUP BY是不确定的.这意味着不应假设非聚合列的结果值是分组之前的事件(即记录集中的任何排序)的结果,尽管实际上在当前实现中它看起来是这样的.
在未来的版本中可能不是这种情况,如果你运行两次,结果甚至可能不一样.明确记录的事实足以让我避免它!
我推断他们打算将algos的实现留给未来优化组合,这可能会在分组之前忽略或破坏记录的原始排序.
从概念上讲,如果将一组记录想象为单个单元而不是单个记录的集合,则有意义.对于非聚合列,可以返回许多可能的值,并且在选择的那一点上没有隐含的条件来选择一个而不是另一个,您必须记住分组之前记录的方式.
我使用这种方法的所有查询都可能在某些时候开始起作用.他们可能会返回未获得该事件最高分的记录的值.
此外,这个错误不会立即显现,因此跟踪最近MySQL升级的原因将需要一段时间.我还可以保证我会忘记这个潜在的陷阱,当它确实发生时所有这些都是一个问题,所以我可能最终会陷入一个较旧的不太安全的MySQL版本,直到我有机会调试它正确等等......痛苦......
JOIN语句中的子选择不使用非聚合列,聚合是确定的,因为它们与整个组相关而不是单个记录.无论记录在分组之前的顺序如何,答案总是相同的.
我使用了一个JOIN声明将这些组与我们感兴趣的各个记录联系起来.在某些情况下,这可能意味着每个组都有多个单独的记录.例如,当涉及两名运动员具有相同最高分的抽奖时,我将要么必须返回两个记录,要么任意选择一个.我相信我们会想要所有得分最高的人,所以我没有提供任何规则来选择两名可能抽签的运动员.
为了选择一个记录作为明显的赢家,我们需要一种能够将获胜者与参赛者分开的方法.作为获得最高分的第一位运动员,我们可能会选择最终的胜利者,而另一位运动员要领先,他们必须更好地获得之前的成绩.
要做到这一点,我们必须有一种方法来确定测试的顺序,所以我们引入一个testId列,它将随着我们得到的每个新结果递增.当我们有了这个时,我们可以执行以下查询......
SELECT a.eventId, athleteId, a.score
FROM tests AS a
JOIN (
-- This select finds the first testId for each score + event combination
SELECT MIN(testId) AS testId, c.eventId, c.score
FROM tests AS c
JOIN (
-- This select finds the top score for each event
SELECT eventId, MAX(score) AS score
FROM tests
GROUP BY eventId
) AS d
ON c.eventId = d.eventId
AND c.score = d.score
GROUP BY eventId, score
) AS b
ON a.testId = b.testId
Run Code Online (Sandbox Code Playgroud)
这里发生的是我们创建表示每个事件的最高得分的组,然后我们内部加入,表示每个得分和事件组合的最低testId的组,最后是与测试表中的记录的内部连接,以获取各个记录.
这也可以写成(具有略微不同的执行计划)如下.
SELECT a.eventId, athleteId, a.score
FROM tests AS a
JOIN (
-- This select finds the top score for each event
SELECT eventId, MAX(score) AS score
FROM tests
GROUP BY eventId
) AS b
ON a.eventId = b.eventId
AND a.score = b.score
JOIN (
-- This select finds the first testId for each score + event combination
SELECT MIN(testId) AS testId, eventId, score
FROM tests
GROUP BY eventId, score
) AS c
ON a.testId = c.testId
Run Code Online (Sandbox Code Playgroud)
解决方案的基本组在较少的SQL中实现了相同的结果,但相比之下它的优化效果非常差.如果我们向表中添加索引,则解决方案的基本组不会使用索引,并且需要在tests表中的所有记录上使用两个filesorts(通过表进行额外运行以使其按顺序排列).但是,上面的原始嵌套子选择查询非常好.
| 归档时间: |
|
| 查看次数: |
184 次 |
| 最近记录: |