我有这个文件表格(这里是简化版):
1 2 3 4 5 6 7 8
| +------+-------+--------------------------------------+
| id | rev | content |
+------+-------+--------------------------------------+
| 1 | 1 | ... |
| 2 | 1 | ... |
| 1 | 2 | ... |
| 1 | 3 | ... |
+------+-------+--------------------------------------+ |
如何为每个ID选择一行,并且只选择最大的Rev?根据以上数据,结果应该包含两行:[1, 3, ...]和[2, 1, ..]。我在使用MySQL。
目前,我在while循环中使用checks来检测和重写结果集中的旧版本。但是,这是实现结果的唯一方法吗?没有SQL解决方案吗?
更新如答案所示,有一个SQL解决方案,这里是一个sqlfiddle演示。
更新2我注意到在添加了上述sqlfiddle之后,问题的上票率已经超过了答案的上票率。这不是我的本意!小提琴是以答案为基础的,尤其是公认的答案。
- 行是否需要对应的content字段?
- 是的,这不会造成任何问题,我已经删掉了很多列,我要把它们加回去。
- @Markbyers我已经编辑了我的答案以满足运营需求。因为我在写,我决定写一个更全面的答案,关于最伟大的N-每小组的主题。
- 这是一个常见的最大N组问题,具有良好的测试和优化解决方案。我更喜欢比尔·卡尔文(原版)的左联解决方案。请注意,对于这个常见问题的大量解决方案可以在一个最官方的资源mysql手册中找到!请参阅常见查询示例:包含特定列的按组最大值的行。
- 检索每个组中最后一条记录的副本
- 对我来说,SELECT DISTINCT ON .... ORDER BY"UserId","Deals".position;工作得更好
乍一看…
您所需要的只是一个带有MAX聚合函数的GROUP BY子句:
从来没有那么简单,是吗?
我刚注意到你也需要content列。
在SQL中,这是一个非常常见的问题:在每一个组标识符的列中查找具有某个最大值的行的整个数据。在我的职业生涯中,我经常听到这种说法。实际上,这是我在当前工作的技术面试中回答的问题之一。
实际上,stackoverflow社区创建了一个标签来处理这样的问题是非常常见的:每组最多N个。
基本上,您有两种方法来解决这个问题:
加入简单的
group-identifier, max-value-in-group子查询
在这种方法中,首先在子查询中找到group-identifier, max-value-in-group(上面已经解决了)。然后,您将表与group-identifier和max-value-in-group上相等的子查询联接:
使用自身左连接,调整连接条件和过滤器
在这种方法中,您离开了与表本身的联接。当然,平等存在于group-identifier中。然后,两个聪明的动作:
第二个联接条件的左侧值小于右侧值
在执行步骤1时,实际具有最大值的行的右侧将有NULL(它是LEFT JOIN),记得吗?然后,我们过滤合并结果,只显示右侧为NULL的行。
所以你最终会得到:
结论
两种方法的结果完全相同。
如果您有两行max-value-in-group用于group-identifier,这两行将在两种方法中产生结果。
这两种方法都是与SQL-ANSI兼容的,因此,无论其"风格"如何,都可以与您最喜欢的RDBMS一起使用。
这两种方法都是性能友好的,但是您的里程可能会有所不同(RDBMS、DB结构、索引等)。所以当你选择一种方法而不是另一种方法时,基准测试。确保你选择对你来说最有意义的那个。
- 第一个版本要简单得多,是不是通过添加更多的列来确定结果?我还应该满足于一个where:select id, max(rev), content, etc., etc., from the_table where proj_id = $pid group by id。
- 我知道MySQL允许您向"分组依据"查询添加非聚合字段,但我发现这有点毫无意义。试着运行这个select id, max(rev), rev from YourTable group by id,你就会明白我的意思了。慢慢来,试着理解它
- 在子选择中注意不要包括任何其他列名,因为它们不会是max行中的列名。MySQL将允许您这样做,尽管其他DB-S(如Oracle)会给您一个例外。对于大多数聚合函数来说,尽管对于max函数似乎是合乎逻辑的,但它并没有意义。所以,小心不要在这上面绊倒;)干杯!
- 首先,感谢您的精彩回答。要添加的内容,请确保您按照最大值或最小值分组。通常,您只按ID分组,但如果您按名称和年龄分组,则您的联接将使用名称=名称,年龄=年龄。我花了几分钟才意识到这是我的问题。
- @我很高兴这个答案能帮到你!我明白你的意思,这就是为什么我称它为group_identifier,它可以是一个或多个列。在你的例子中,group_identifier是名字和年龄的组合。
- 如果每行没有唯一的标识符字段,该如何操作?
- Totty…你不能。如果要对所有数据库表执行智能查询,则需要主键。
- 第二个解决方案是一个非常有趣的方法。在我的数据集中,运行需要4分钟,而子查询则需要0.23秒。是否有任何索引可以建议这样做有帮助?
- @WhiteAtom绝对:索引ID和Rev(相应地调整名称)。可能是这样的,对两者进行索引也会有所帮助
- 嗯,不掷骰子。手边花了6分钟。我将索引单独添加到id和rev中,花费了4分钟。总共需要3:53,两个都需要3:52。有趣的是,这两种方法是不同的。我想知道在什么情况下找没有更大的行更快?
- @怀特纳姆很有趣,应该是这样的。你试过为Rev创建描述索引吗?也许您应该在一个新问题中发布您的数据结构和估计的数据负载。请告诉我
- 空值有时很慢。不管怎样,我发现第二个解决方案既有趣又聪明。伟大的回答!
- 但是,如何让它只返回每组一行呢?这些答案是否会返回每组中比较值等于最大值的每一行?例如,假设在OP的数据集中有第二行id=1,rev=3。它不会返回id=1,rev=3的两行吗?
- @你说的是领带。这个解决方案不打破联系:"如果在组中有两行的组标识符的最大值,这两行将在两种方法中都得到结果。"这是根据设计的,因为这个解决方案是通用的。你是负责打领带的人,这意味着当两个最大值相等时,你必须决定你要使用什么作为打领带的人(可能是另一个字段?)然后你可以调整第二种方法来考虑这个问题。
- 我只是想知道我在下面的答案中输入的查询是否效率较低,或者不等同于在这个答案中输入的查询,如果是,为什么?
- @阿德里安卡内罗,你是说不可能用第一个版本打破平局?
- 值得一提的是,如果您使用代码进行随机查找(例如,作为视图的基础),则基于子查询的解决方案速度非常慢(因为每次查找都会执行子查询),而左联接解决方案的效果很好…对于这个问题,还有一个基于相关子查询的解决方案(它可以避免leftjoin的大组问题)。看看我的答案。请随意添加到您的答案中…
- @罗伯特克里斯特擅自与第一版断绝关系,只需在第一版的SELECT之后加上DISTINCT ON (yt.id)。这使我的询问时间增加了一倍。所以,我不打领带,因为在我的情况下几乎不可能打领带。
- 为什么第一个解决方案会起作用?每个由一行组成的组不会运行max函数,而不是将所有行作为一个整体运行。
- 我很欣赏第二个解决方案,因为District DQL不支持join语句中的子查询(如第一个解决方案),所以这是一个非常有帮助的解决方案!
- @德语我自己也很难理解这个查询,但我相信GROUP BY子句会导致返回所有行,而不是只返回一行。
- 在这里使用Mariadb。不知道为什么使用HAVING不能产生正确的结果。即SELECT a, b, max(b) as max_b FROM table GROUP BY a HAVING b=max_b。查询有效,但某些行只是丢失了
- @阿德里安·卡内罗:非常感谢你的回答,但我认为你的第一个解决方案应该是SELECT id, MAX(rev), rev,我得到了:"Rev列必须在分组列表中"。我的数据库是否缺少(不是MySQL),这不是标准的SQL,或者是否发生了其他事情?谢谢!
- 第二种方法对我不起作用。它显示的结果与我刚从表中运行简单选择的结果相同
- 添加列内容有什么问题?为什么不起作用?
- 我在过去使用过这两种解决方案,对于小数据集,它们工作得很好——但是——它们的伸缩性不好。即使有索引等…最好将查询分解为两个较小的步骤并填充一个临时表。
- 谢谢!!我采用了内部连接方法,工作正常。
- @Adriancarneiro我不明白为什么在2列而不是1列(即内部联接方法的ID)上存在相等。如果您只在ID或Rev上加入而不是同时在两者上加入,该方法是否有效?有人能解释一下吗?
- 是否有人知道如何改进该查询(尤其对第一种方法感兴趣),以便为每个实体返回一行,即使我们有几个相同的最大值。也就是说,当n最大时,每组最大1个。我想Distint可能会有帮助,但似乎很慢
- 还有第三种选择(没有连接),我之前没有提到过:合并rev和id,选择MAX(),然后提取id。详情如下。
- 我在3M行的表上同时尝试了SQL查询,第一(1)个查询比第二(2)个查询快得多。""更多"意味着我没有等到第二个请求完成。(1)SELECT a.* FROM history a JOIN (SELECT productId, MAX(updatedAt) AS updatedAt FROM history WHERE updatedAt BETWEEN '2018-12-11 00:00:00' AND '2018-12-11 23:59:58' GROUP BY productId ) AS b ON a.productId = b.productId AND a.updatedAt = b.updatedAt(2)SELECT a.* history a LEFT OUTER JOIN history b ON a.productId = b.productId AND a.updatedAt < b.updatedAt WHERE b.id IS NULL AND a.updatedAt BETWEEN '...' AND '...'。
- 对于不注意的读者(我属于我自己):尽管事实上你在寻找最大的rev,但声明应该是a.rev < b.rev(乍一看,它看起来好像你在寻找最小的修订版),因为后来你添加了WHERE b.rev IS NULL,这给了整个命令以下含义:"没有rev"。比a.rev大
- 就性能而言,第二种方法甚至不是第一种方法的50%(就MySQL而言)。
我的首选是使用尽可能少的代码…
你可以用IN来做。试试这个:
在我看来,这并不复杂…易于阅读和维护。
- 好奇—我们可以在哪个数据库引擎中使用这种类型的WHERE子句?这在SQL Server中不受支持。
- Oracle&mysql(不确定其他数据库,抱歉)
- 也适用于PostgreSQL。
- 已确认在DB2中工作
- 不适用于sqlite。
- 提供的答案是有效的ansiiso-sql!!!!
- 太棒了,谢谢。在SQLDeveloper(Oracle)工作
- 记住,如果您使用的是MySQL,并且这个查询返回类似于SELECT list is not in GROUP BY clause and contains nonaggregated column 'db.t1.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by的结果,那么您需要通过SQL模式(例如:SET sql_mode = ''禁用唯一的"完整"组。
- 在MySQL中,这个构造的性能很差:WHERE (a,b) IN ( SELECT ... ),原因有两个:(a,b)和IN(SELECT。
- 更新:Percona5.6.22以合理的方式优化这一点—顺序(n),其中n是表中的行数。MySQL5.5.43的工作顺序很糟糕(n*n)。Mariadb 10.0.28做得很好——没有扫描表!
- 在查询结束时再添加一个:GROUP BY id?这似乎可以防止每个ID有多个记录。这是一种安全的方法吗?
- 也适用于Spark SQL。:)
- 可悲的是,这在普雷斯托还不起作用:(
- 也可以与MongoDB一起使用
另一种解决方案是使用相关的子查询:
在(id,rev)上有一个索引会使子查询几乎变成一个简单的查找…
以下是与@adriancarneiro's answer(subquery,leftjoin)中的解决方案的比较,基于mysql测量,innodb表约为100万条记录,组大小为:1-3。
对于全表扫描,子查询/leftjoin/相关计时以6/8/9相互关联,而对于直接查找或批处理(id in (1,2,3)),子查询比其他查询慢得多(由于重新运行子查询)。然而,我无法在速度上区分leftjoin和相关的解决方案。
最后一点要注意的是,当leftjoin在组中创建n*(n+1)/2个联接时,其性能会受到组大小的严重影响…
- 这是迄今为止唯一一个符合我需要的方式的,谢谢(需要按名称匹配,而不是按ID匹配)
- 如果rev不是唯一的,我认为这不起作用。
- @pita不,即使rev不是唯一的,它也能工作。
- 提到简单查找所需的索引很好(显然不能再在注释中加1)
- However I couldnt differentiate between leftjoin and correlated solutions in speed.—我对SQL Server也一样。
- 我不明白为什么这不是解决办法。如果没有别的,它是唯一可读的。
我惊讶于没有人提供SQL窗口函数解决方案:
在SQL标准中添加了ANSI/ISO标准SQL:2003,后来又扩展了ANSI/ISO标准SQL:2008,现在所有主要供应商都可以使用窗口(或窗口)功能。有更多类型的秩函数可用于处理一个关联问题:RANK, DENSE_RANK, PERSENT_RANK。
- 型我认为它不那么直观,而且可能不那么清晰——但它肯定能起作用/成为一个解决方案。
- 型直觉是一件棘手的事情。我发现它比其他答案更直观,因为它构建了明确的数据结构来回答这个问题。但是,直觉是偏见的另一面…
- 型这可能在Mariadb10.2和MySQL8.0.2中有效,但在以前不适用。
- 型最后,我开始纳闷为什么这个不在这里。这远比本页上绝大多数"老帽子"的答案更"直观",而且在几乎所有情况下都更有效,因为它只需要传递一次数据。大多数数据库现在都支持这些标准的窗口函数(MySQL已经很晚了,但从V8开始就支持)。
- 型我不知道这个功能存在。今天晚上深入阅读了一大堆手册。这比左加入更有意义(仅仅是因为缺乏挫折感)。
- 型我建议不要在bigquery中使用它;order by子句意味着需要在单个节点上处理所有数据,从而导致大型表的"超出资源"。
我不能保证它的性能,但这里有一个技巧是受Microsoft Excel的限制而启发的。它有一些好的特点
好东西
- 它应该只强制返回一个"最大记录",即使有平局(有时很有用)
- 不需要加入
途径
它有点难看,要求您了解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函数(在一种形式或其他几乎所有地方都可用)很容易转换回原始数字。
我认为这是最简单的解决方案:
- SELECT *返回所有字段。
- FROM Employee:已搜索的表。
- (SELECT *...)子查询:返回所有人员,按薪资排序。
- GROUP BY employeesub.Salary:强制每个员工的最高排序工资行作为返回结果。
如果你恰好只需要一行,那就更容易了:
我还认为最容易分解、理解和修改到其他目的:
- ORDER BY Employee.Salary DESC:按工资顺序排列结果,首先是最高工资。
- LIMIT 1:只返回一个结果。
理解这种方法,解决这些类似的问题就变得微不足道了:让工资最低的员工(将DESC改为ASC、让收入最高的10名员工(将LIMIT 1改为LIMIT 10、通过另一个字段进行排序(将ORDER BY Employee.Salary改为ORDER BY Employee.Commission等)。
- 型这不能回答问题。问题是如何在组查询中获取一行的数据(正如我们所问的那样,"每个ID一行"),其中值x是每组行中的最大值。例如,一个客户订单表,其中每个客户有多个订单,您希望从中检索每个客户的最大订单。您的查询很可能会为每个客户返回多行(例如,如果两个最大的订单是由同一个客户下的)。
- 型"每个ID一行"<--请继续阅读,您会看到"而且只有最棒的"。这在逻辑上等同于最伟大的。
- 型是的,但上面写着"和"。这意味着每个ID都有一行需求,而且只有最大的需求。使用这个答案将不能满足第一个要求。此外,这个问题意味着需要为所有ID检索一个记录。这个答案需要预先知道ID的数量(为了配置限制),这将需要额外的代码。这个问题的目标被明确地表述为寻找一个只包含SQL的解决方案。最后,即使知道唯一ID的数目,如果max值出现多次,limit子句也会出错。
- 我并不像在原来的帖子中那样有着完全相同的情况,但这是迄今为止我遇到的最容易理解、最直截了当、最有效的解决方案。我很惊讶所有的极客和怪胎都试图用复杂/奇怪的问题来吹嘘,从而超越对方。
- @Aaron J Spettner:我更新了一个解决方案,可以直接满足OP的需求。
- 这是一个黑客解决方案,在以后的MySQL版本中完全崩溃,在服务器配置中启用了ONLY_FULL_GROUP_BY的服务器上无法工作…sqlfiddle.com/!9/215CD/4
- @Raymond Nijland:你可以通过将"*"更改为你真正想要的字段来实现它。以上只是概念的证明。我修改了查询以使用您的MySQL模式:sqlfiddle.com/!9/215CD/6
- @holdoffhunger您的最后一个小提琴查询相当于SELECT DISTINCT rev FROM Table1;-sqlpiddle.com/!9/215CD/9
像这样?
- 少一点的人不会剪掉它吗?
- 如果他们工作,那么他们也很好。
- WHERE yourtable是做什么的?
- 这似乎是最快的(有适当的索引)。
既然这是关于这个问题最流行的问题,我也会在这里发布另一个答案:
看起来有更简单的方法可以做到这一点(但仅在MySQL中):
请相信用户波西米亚在这个问题的答案,提供这样一个简洁和优雅的回答这个问题。
Edit: though this solution works for many people it may not be stable in the long run, since MySQL doesn't guarantee that GROUP BY statement will return meaningful values for columns not in GROUP BY list. So use this solution at your own risk!
- 但这是错误的,因为不能保证内部查询的顺序意味着任何内容,也不能保证group by始终采用遇到的第一行。至少在MySQL中,我会假设所有其他的。事实上,我是在这样的假设下,MySQL只会忽略整个order by。任何未来版本或配置更改都可能会破坏此查询。
- @Jannes这是一句有趣的话:)我欢迎你回答我的问题,提供证据:stackoverflow.com/questions/26301877/&hellip;
- @Jannes关于Group By的问题,并没有保证在遇到第一行时——你完全正确——发现了这个问题bugs.mysql.com/bug.php?ID=71942,要求提供此类担保。将立即更新我的答案
- 我想我记得我从哪里得到的命令被丢弃了:MySQL通过联合实现了这一点如果你通过内部查询进行排序,它只是忽略了:dev.mysql.com/doc/refman/5.0/en/union.html说"如果order by在select中出现而没有限制,它会被优化掉,因为它无论如何都不会有任何效果。"我还没有看到这样的查询语句。这里有问题,但我不明白为什么它不能做到。
我喜欢使用基于NOT EXIST的解决方案来解决这个问题:
这将选择组中最大值的所有记录,并允许您选择其他列。
- 是的,不存在通常是首选的方式,而不是左连接。在旧版本的SQL Server中,速度更快,但我认为现在没有什么不同。我通常选择1而不是选择*,同样,因为在以前的版本中它更快。
不是mysql,但是对于发现这个问题并使用sql的其他人来说,解决最大的n-per-group问题的另一种方法是在ms-sql中使用Cross Apply。
下面是sqlfiddle中的一个示例
- 与其他方法相比非常慢-Group By,Windows,Not exists
第三种解决方案是MySQL特有的,看起来像这样:
是的,它看起来很糟糕(转换为字符串和返回等),但根据我的经验,它通常比其他解决方案更快。也许这只是用于我的用例,但我已经在有数百万条记录和许多唯一ID的表上使用了它。可能是因为MySQL在优化其他解决方案方面非常糟糕(至少在我提出这个解决方案的5.0天内)。
一件重要的事情是,组concat对于它可以建立的字符串具有最大长度。您可能希望通过设置group_concat_max_len变量来提高此限制。请记住,如果您有大量的行,这将是缩放的限制。
无论如何,如果内容字段已经是文本,那么上面的内容不能直接工作。在这种情况下,您可能希望使用不同的分隔符,例如 。你也会更快地遇到group_concat_max_len限制。
如果select语句中有许多字段,并且希望通过优化代码为所有这些字段提供最新值:
- 型对于小表来说,这是正常的,但是对于整个数据集来说,需要6次传递,因此对于大表来说,这并不快。
- 型这是我需要的查询,因为还涉及其他列。
我想,你想要这个?
SQL小提琴:这里检查
这个怎么样?
此解决方案只允许您选择一个选项,因此速度更快。根据sqlfiddle.com上的测试,它只适用于mysql和sqlite(对于sqlite remove desc)。也许它可以调整到我不熟悉的其他语言上。
- 这似乎不适用于一般情况。而且,它在PostgreSQL中根本不起作用,返回:ERROR: column"your table.reb" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: SELECT *
- 抱歉,我没有第一次澄清它的工作语言。
我会用这个:
子查询select可能不太有效,但in join子句似乎可用。我不是优化查询的专家,但我在MySQL、PostgreSQL、Firebird上都做过尝试,而且效果很好。
您可以在多个联接和WHERE子句中使用此模式。这是我的工作示例(用表格"firmy"解决与您的问题相同的问题):
这是在有十几岁的记录的桌子上被问到的,而且在真正不太强大的机器上只需要不到0,01秒的时间。
我不会用in子句(正如上面提到的)。在中,它被赋予与常量的短列表一起使用,而不是作为建立在子查询上的查询过滤器。这是因为在中对每个扫描的记录执行子查询,这会使查询占用很长的时间。
- 我认为将该子查询用作CTE至少可以提高性能
- 你好!对我来说,似乎你的第一个查询需要…最后返回and o.id = t.id(子查询返回id)。不是吗?
另一种方法是在over-partition子句中使用MAX()分析函数。
本文中已经记录的另一个ROW_NUMBER()过度分区解决方案是
这2个选项在Oracle10g上很好地工作。
max()解决方案比ROW_NUMBER()解决方案运行得更快,因为MAX()的复杂性是O(n),而ROW_NUMBER()的复杂性是O(n.log(n)),其中n表示表中的记录数!
这是个很好的方法
使用以下代码:
将rev字段按相反的顺序排序,然后按ID分组,ID给出每个分组的第一行,即rev值最高的一行。
在http://sqlfiddle.com/中使用以下数据进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CREATE TABLE table1
(`id` int, `rev` int, `content` varchar(11));
INSERT INTO table1
(`id`, `rev`, `content`)
VALUES
(1, 1, 'One-One'),
(1, 2, 'One-Two'),
(2, 1, 'Two-One'),
(2, 2, 'Two-Two'),
(3, 2, 'Three-Two'),
(3, 1, 'Three-One'),
(3, 3, 'Three-Three')
; |
这在MySQL5.5和5.6中给出了以下结果
1 2 3 4
| id rev content
1 2 One-Two
2 2 Two-Two
3 3 Three-Two |
- 型这项技术曾经有效,但现在不再有效了。见mariadb.com/kb/en/mariadb/&hellip;
- 型最初的问题标签是"mysql",我已经非常清楚地声明我的解决方案在sqlfiddle.com中用mysql 5.5和5.6进行了测试。我提供了独立验证解决方案的所有步骤。我没有错误地声称我的解决方案适用于Mariadb。Mariadb不是MySQL,它只是两个不同公司拥有的MySQL的替代品。你的评论将有助于任何试图在Mariadb中实现它的人,但我的帖子绝对不值得投反对票,因为它清楚地回答了被问到的问题。
- 型是的,它在旧版本中工作。我以前也用过这种方法,但当它停止工作时,我就被烧掉了。还有MySQL(5.7版?)也将忽略子查询中的ORDER BY。因为很多人都会阅读你的答案,所以我正试图引导他们远离一种在未来会被打破的技术。(我没有给你-1票。)
- 型测试证明什么都没有。除同一子查询中的限制外,子查询中的ORDER BY没有保证的效果。即使保留了命令,group by也不会保留它。即使保留了非标准分组,也会指定仅依赖于禁用的"完整分组"的非标准分组,以便为非分组列返回组中的某些行,但不一定是第一行。所以您的查询不正确。
这是另一个解决办法,希望它能帮助别人
这些答案对我都不起作用。
这就是我的工作。
这是另一个只检索具有该字段最大值的字段的记录的解决方案。这适用于我工作的平台sql400。在本例中,字段field5中最大值的记录将由以下SQL语句检索。
我喜欢通过按列对记录进行排序来完成这项工作。在这种情况下,按id分组,对rev值进行排序。rev越高,排名就越低。所以最高的rev将排名为1。
1 2 3 4 5 6 7 8 9 10 11
| select id , rev , content
from
(select
@rowNum := if(@prevValue = id , @rowNum +1, 1) as row_num ,
id , rev , content ,
@prevValue := id
from
(select id , rev , content from YOURTABLE order by id asc, rev desc) TEMP ,
(select @rowNum := 1 from DUAL ) X,
(select @prevValue := -1 from DUAL ) Y) TEMP
where row_num = 1; |
不确定引入变量是否会使整个过程变慢。但至少我没有两次询问YOURTABLE。
- 在MySQL中只尝试过这种方法。Oracle也有类似的排名记录功能。这个想法也应该奏效。
- 型在MySQL中,在select语句中读取和写入变量是未定义的,尽管某些特定版本正好给出了您可能期望的涉及case表达式的某些语法的答案。
解释
这不是纯SQL。这将使用SQLAlchemy ORM。
我来这里是为了寻求sqlacalchemy的帮助,所以我将使用python/sqlacalchemy版本复制adrian carneiro的答案,特别是外部连接部分。
此查询回答以下问题:
"您能把这组记录中版本号最高的记录(基于相同的ID)返回给我吗?"
这允许我复制记录,更新它,增加它的版本号,并以一种我可以显示随时间变化的方式复制旧版本。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| MyTableAlias = aliased (MyTable )
newest_records = appdb. session.query (MyTable ).select_from (join(
MyTable ,
MyTableAlias ,
onclause =and_ (
MyTable.id == MyTableAlias.id ,
MyTable.version_int < MyTableAlias.version_int
),
isouter =True
)
).filter (
MyTableAlias.id == None ,
). all() |
在PostgreSQL数据库上测试。
我用下面的内容来解决我自己的问题。我首先创建了一个临时表,并插入了每个唯一ID的最大rev值。
然后,我将这些最大值(temp1)加入到所有可能的ID/内容组合中。通过这样做,我自然地过滤掉了非最大ID/内容组合,并且只剩下了每个组合的最大rev值。
当您将rev和id组合为MAX()的一个maxRevId值,然后将其拆分为原始值时,可以不使用联接进行选择:
当有一个复杂的联接而不是一个表时,这就特别快。使用传统方法,复杂连接将执行两次。
当rev和id为INT UNSIGNED位(32位),组合值适合BIGINT UNSIGNED位(64位)时,上述组合与位函数简单。当id和rev大于32位值或由多列组成时,需要将该值合并为二进制值,并为MAX()添加适当的填充。