**想改进这篇文章吗?**提供这个问题的详细答案,包括引文和解释为什么你的答案是正确的。没有足够细节的答案可能会被编辑或删除。
这个问题在这里已经有答案了:
检索每个组中的最后一条记录-mysql(29个答案)
去年关门了。
我有这个文件表(这里是简化版):
+------+-------+--------------------------------------+
| id | rev | content |
+------+-------+--------------------------------------+
| 1 | 1 | ... |
| 2 | 1 | ... |
| 1 | 2 | ... |
| 1 | 3 | ... |
+------+-------+--------------------------------------+
如何为每个id选择一行,并且只选择最大的rev?
对于上述数据,结果应包含两行: [1, 3, ...]
以及 [2, 1, ..]
. 我正在使用mysql。
目前我使用支票 while
循环从结果集中检测和重写旧的rev。但是,这是实现这一结果的唯一方法吗?没有sql解决方案吗?
更新
正如答案所示,这里有一个sql解决方案,这里有一个sqlfiddle演示。
更新2
我注意到在添加上述SQLFIDLE之后,问题的得票率已经超过了答案的得票率。这不是我的本意!小提琴以答案为基础,尤其是公认的答案。
14条答案
按热度按时间kfgdxczn1#
我不能保证它的性能,但这里有一个技巧是受microsoftexcel的局限性启发的。它有一些很好的特点
好东西
它应该强制只返回一个“最大记录”,即使有平局(有时有用)
它不需要连接
方法
这有点难看,需要了解rev列的有效值范围。假设rev列是一个介于0.00和999之间的数字,包括小数点,但小数点右边只有两位数(例如,34.17将是一个有效值)。
要点是,通过字符串连接/打包主比较字段以及所需的数据来创建一个合成列。通过这种方式,可以强制sql的max()聚合函数返回所有数据(因为它已打包到单个列中)。然后你得把数据解包。
下面是上面用sql编写的示例的外观
打包首先强制rev列是一个已知字符长度的数字,而不考虑rev的值,例如
3.2变为1003.201
57变为1057.001
923.88变为1923.881
如果操作正确,两个数字的字符串比较应该产生与两个数字的数字比较相同的“max”,并且使用substring函数很容易将其转换回原始数字(几乎任何地方都可以使用一种或另一种形式)。
cqoc49vn2#
我感到震惊的是,没有答案提供sql窗口函数解决方案:
添加在sql标准ansi/iso标准中sql:2003 and 后来扩展为ansi/iso标准sql:2008,窗口(或窗口)功能可与所有主要供应商现在。有更多类型的秩函数可用于处理平局问题:
RANK, DENSE_RANK, PERSENT_RANK
.zphenhs43#
另一种解决方案是使用相关子查询:
在(id,rev)上有一个索引会将子查询呈现为一个简单的查找。。。
以下是与@adriancarneiro的答案(subquery,leftjoin)中的解决方案的比较,这些解决方案是基于mysql对innodb表的测量,innodb表有约100万条记录,组大小为:1-3。
而对于全表扫描,当涉及到直接查找或批处理时,子查询/leftjoin/correlated计时相互关联为6/8/9(
id in (1,2,3)
),子查询比其他查询慢得多(因为重新运行子查询)。但是,我无法区分leftjoin和相关解决方案的速度。最后要注意的是,由于leftjoin在组中创建n*(n+1)/2个连接,因此其性能会受到组大小的严重影响。。。
kx7yvsdv4#
乍一看。。。
你只需要一个
GROUP BY
带有MAX
聚合函数:从来没有这么简单,是吗?
我刚注意到你需要
content
列也是。在sql中,这是一个非常常见的问题:根据某个组标识符,在列中查找具有某个最大值的行的整个数据。在我的职业生涯中我经常听到这样的话。实际上,这是我在当前工作的技术面试中回答的问题之一。
实际上,stackoverflow社区创建了一个标签来处理这样的问题是很常见的:greatest-n-per-group。
基本上,有两种方法可以解决这个问题:
与简单组标识符连接,组子查询中的最大值
在这种方法中,首先要找到
group-identifier, max-value-in-group
(上面已经解决)在子查询中。然后将表连接到子查询,两个查询上的值相等group-identifier
以及max-value-in-group
:用self左连接,调整连接条件和过滤器
在这种方法中,您将表与其自身连接起来。平等的原则
group-identifier
. 然后,两个聪明的动作:第二个连接条件是左侧值小于右侧值
执行步骤1时,实际具有最大值的行将具有
NULL
在右边(这是一个LEFT JOIN
,记得吗?)。然后,我们过滤连接的结果,只显示右侧所在的行NULL
.所以你最终会得到:
结论
两种方法的结果完全相同。
如果你有两排
max-value-in-group
为了group-identifier
,两种方法的结果中都将包含这两行。这两种方法都是sqlansi兼容的,因此,无论其“风格”如何,都可以与您喜爱的rdbms一起工作。
这两种方法对性能也很友好,但是您的里程数可能会有所不同(rdbms、db结构、索引等)。所以当你选择一种方法而不是另一种方法时,基准测试。一定要挑对你最有意义的。
x8diyxa75#
像这样的?
arknldoa6#
我倾向于使用尽可能少的代码。。。
你可以用
IN
试试这个:在我看来没那么复杂。。。更易于阅读和维护。
yduiuuwa7#
另一种方法是使用
MAX()
过分句中的解析函数其他的
ROW_NUMBER()
在这篇文章中已经提到的过分区解决方案是这两个选择在oracle 10g上运行良好。
max()解决方案的运行速度肯定比
ROW_NUMBER()
解决方案因为MAX()
复杂性是O(n)
而ROW_NUMBER()
复杂性是最低限度的O(n.log(n))
哪里n
表示表中的记录数!kupeojn68#
我很少提到的第三种解决方案是特定于mysql的,如下所示:
是的,它看起来很糟糕(转换为字符串和返回等),但根据我的经验,它通常比其他解决方案更快。也许这只是针对我的用例,但我已经在有数百万条记录和许多唯一id的表上使用了它。可能是因为mysql在优化其他解决方案方面做得很差(至少在我提出这个解决方案的5.0年代是这样)。
最重要的一点是,groupconcat有一个它可以建立的字符串的最大长度。您可能希望通过设置
group_concat_max_len
变量。请记住,如果您有大量的行,这将是缩放的限制。不管怎样,如果内容字段已经是文本,那么上面的方法就不能直接起作用。在这种情况下,可能需要使用不同的分隔符,例如\0。你还会遇到
group_concat_max_len
限制更快。ua4mk5z49#
我想,你想要这个?
sql fiddle:检查这里
vtwuwzda10#
我喜欢使用
NOT EXIST
-基于此问题的解决方案:这将选择组中具有最大值的所有记录,并允许您选择其他列。
jdzmm42g11#
因为这是关于这个问题最流行的问题,所以我也会在这里重新发布另一个答案:
看起来有更简单的方法可以做到这一点(但仅限于mysql):
请回答信用卡问题
rqmkfv5c12#
最干净的解决方案
小提琴
mysql的更新版本随附
ONLY_FULL_GROUP_BY
在默认情况下启用,并且这里的许多解决方案在使用此条件进行测试时将失败。即便如此,我们也可以简单地选择
DISTINCT
某个独一无二的领域,MAX(
还有哪些领域需要选择)
,(
*某地)
,并且不必担心了解结果或查询的工作方式:id max(Table1.rev) max(Table2.content)
1 3 ..d
2 1 ..b
SELECT *
FROM
(SELECT *
FROM Employee
ORDER BY Salary DESC)
AS employeesub
GROUP BY employeesub.Salary;
```
SELECT *
:返回所有字段。FROM Employee
:在上搜索的表。(SELECT *...)
子查询:返回所有人员,按薪资排序。GROUP BY employeesub.Salary
:强制将每个员工的排名靠前的薪资行作为返回结果。唯一行解决方案
请注意关系数据库的定义:“表中的每一行都有自己的唯一键。”这意味着,在问题的示例中,id必须是唯一的,在这种情况下,我们可以:
希望这是一个解决问题的解决方案,可以帮助每个人更好地理解数据库中发生的事情。
tkclm6bt13#
mmvthczy14#
不是mysql,而是对于其他发现这个问题并使用sql的人来说,另一种解决最大的n-per-group问题的方法是使用
Cross Apply
在ms sql中下面是sqlfiddle中的一个示例