SQL 仅选择列上具有最大值的行 [duplicate]

SQL select only rows with max value on a column [duplicate]

提问人:Majid Fouladpour 提问时间:10/13/2011 最后编辑:Zoe is on strikeMajid Fouladpour 更新时间:7/1/2022 访问量:2017211

问:

想改进这篇文章吗?提供此问题的详细答案,包括引文和解释为什么您的答案是正确的。没有足够细节的答案可能会被编辑或删除。

我有这张文件表(简化版在这里):

编号 转速 内容
1 1 ...
2 1 ...
1 2 ...
1 3 ...

如何为每个 id 选择一行,并且只选择最大转数?
使用上述数据,结果应包含两行:和 .我正在使用MySQL
[1, 3, ...][2, 1, ..]

目前,我在循环中使用检查来检测和覆盖结果集中的旧转速。但这是达到结果的唯一方法吗?难道没有SQL解决方案吗?while

mysql sql 聚合函数 greatest-n-per-group groupwise-maximum

评论

1赞 Mark Byers 10/13/2011
是否需要该行的相应字段?content
0赞 Majid Fouladpour 10/13/2011
是的,这不会造成问题,我已经剪掉了许多要添加回来的列。
1赞 Adriano Carneiro 10/13/2011
@MarkByers我已经编辑了我的答案以符合 OP 的需要。既然我这样做了,我就决定写一个关于每组最多 n 个主题的更全面的答案。
2赞 Tomas 4/28/2014
这是常见的每组最大 n 个问题,它有经过充分测试和优化的解决方案。我更喜欢 Bill Karwin 的左联接解决方案原始帖子)。请注意,令人惊讶的是,在最官方的来源之一MySQL手册中可以找到此常见问题的一堆解决方案!请参阅常见查询示例:: 包含特定列的按组最大值的行
2赞 Tomas 7/9/2014
重复项 检索每个组中的最后一条记录

答:

2543赞 Adriano Carneiro 10/13/2011 #1

乍一看...

您只需要一个带有聚合函数的子句:GROUP BYMAX

SELECT id, MAX(rev)
FROM YourTable
GROUP BY id

事情从来都不是那么简单,不是吗?

我刚刚注意到你也需要这个专栏。content

这是 SQL 中一个非常常见的问题:在每个组标识符的列中查找具有最大值的行的整个数据。在我的职业生涯中,我经常听到这种说法。实际上,这是我在当前工作的技术面试中回答的问题之一。

实际上,Stack Overflow 社区创建了一个标签来处理这样的问题,这太常见了:

基本上,有两种方法可以解决这个问题:

使用简单的子查询进行联接group-identifier, max-value-in-group

在这种方法中,首先在子查询中找到(上面已经解决的)。然后,将表连接到子查询,两者相等,并且:group-identifier, max-value-in-groupgroup-identifiermax-value-in-group

SELECT a.id, a.rev, a.contents
FROM YourTable a
INNER JOIN (
    SELECT id, MAX(rev) rev
    FROM YourTable
    GROUP BY id
) b ON a.id = b.id AND a.rev = b.rev

使用自身进行左联接,调整联接条件和过滤器

在这种方法中,您将表与自身连接起来。平等在.然后,2个聪明的举动:group-identifier

  1. 第二个连接条件是左侧值小于右侧值
  2. 当您执行第 1 步时,实际具有最大值的行将位于右侧(这是一个,还记得吗?然后,我们过滤联接结果,仅显示右侧所在的行。NULLLEFT JOINNULL

所以你最终会得到:

SELECT a.*
FROM YourTable a
LEFT OUTER JOIN YourTable b
    ON a.id = b.id AND a.rev < b.rev
WHERE b.id IS NULL;

结论

这两种方法都会带来完全相同的结果。

如果有两行 for ,则这两种方法的结果中都将有两行。max-value-in-groupgroup-identifier

这两种方法都与SQL ANSI兼容,因此,无论其“风格”如何,都可以与您喜欢的RDBMS一起使用。

这两种方法对性能也很友好,但您的里程可能会有所不同(RDBMS、数据库结构、索引等)。因此,当您选择一种方法而不是另一种方法时,请进行基准测试。并确保你选择一个对你最有意义的。

评论

8赞 mk3009hppw 3/22/2021
这是一个非常糟糕的主意,因为您想要最大化的字段可能是双精度,并且比较双精度是否相等是不确定的。我认为只有 O(n^2) 算法在这里有效。
0赞 binoculars 8/27/2021
@Adriano如果有一个额外的列,这将如何工作,并且您想将结果限制在该列内?我想过滤器应该很早就发生,以避免它将不相关的东西连接在一起,这些内容将在以后被抛弃?user_iduser_iduser_id
0赞 a3nm 9/16/2021
我不确定这两种方法是否会“带来完全相同的结果”:我认为第二种方法将保留字段为 NULL 的记录(它们在连接中没有匹配项),但第一种方法不会保留它们(它们的转速不是最大值,因此它们没有被选中)。rev
7赞 Marcos 12/8/2021
另一种方法是使用窗口函数。它们似乎提供了更好的性能。我会做这样的事情:SELECT DISTINCT id, MAX(rev) OVER (PARTITION BY id), FIRST_VALUE(content) OVER (PARTITION BY id ORDER BY rev DESC) FROM YourTable
9赞 Ilmari Karonen 12/8/2021
@mk3009hppw:比较双打是否相等完全是确定性的,尽管认为它在某种程度上不是这样的想法是一个常见的误解。人们通常的意思是(如果他们不只是鹦鹉学舌地从其他地方听到的东西)是,由于四舍五入,不精确的浮点计算(可能像 0.1 + 0.2 一样简单)可能不会完全返回“预期”结果 (0.3),或者比较具有不同精度的数值类型可能会出乎意料。但这些都没有发生在这里。
1赞 jederik 9/2/2022
如果有多行具有相同的值,它会怎么做?至少对于第一种方法,我很确定我会为每个组获得多个条目(所有行都有最大值)。revrev
394赞 Kevin Burton 10/13/2011 #2

我的偏好是使用尽可能少的代码......

您可以使用以下方法执行此操作:IN

SELECT * 
FROM t1 WHERE (id,rev) IN 
( SELECT id, MAX(rev)
  FROM t1
  GROUP BY id
)

在我看来,它没有那么复杂......更易于阅读和维护。

评论

42赞 Kash 11/18/2011
好奇 - 我们可以在哪个数据库引擎中使用这种类型的 WHERE 子句?这在 SQL Server 中不受支持。
34赞 Kevin Burton 11/18/2011
Oracle & MySQL(对不起,不确定其他数据库)
38赞 lcguida 1/16/2014
也适用于 PostgreSQL。
21赞 coderatchet 1/29/2014
确认在 DB2 中工作
28赞 Marcel Pfeiffer 10/27/2014
不适用于 SQLite。
25赞 Marc B 10/13/2011 #3

像这样的东西?

SELECT yourtable.id, rev, content
FROM yourtable
INNER JOIN (
    SELECT id, max(rev) as maxrev
    FROM yourtable
    GROUP BY id
) AS child ON (yourtable.id = child.id) AND (yourtable.rev = maxrev)

评论

0赞 Majid Fouladpour 10/13/2011
没有加入的人不会削减它吗?
1赞 Marc B 10/13/2011
如果它们有效,那么它们也很好。
0赞 Salman A 2/13/2019
这似乎是最快的(具有适当的索引)。
0赞 Gwen Au 9/9/2020
另一方面,没有孩子让我着迷!
48赞 David Foster 6/30/2013 #4

我不能保证性能,但这里有一个受 Microsoft Excel 局限性启发的技巧。它有一些不错的功能

好东西

  • 即使有平局,它也应该只强制返回一个“最大记录”(有时很有用)
  • 它不需要联接

方法

它有点丑陋,需要您对 rev 列的有效值范围有所了解。让我们假设我们知道 rev 列是一个介于 0.00 和 999 之间的数字,包括小数点,但小数点右边只有两位数字(例如,34.17 将是一个有效值)。

事情的要点是,您可以通过字符串连接/打包主要比较字段以及所需的数据来创建一个合成列。通过这种方式,您可以强制 SQL 的 MAX() 聚合函数返回所有数据(因为它已打包到单个列中)。然后,您必须解压缩数据。

下面是上面用 SQL 编写的示例的样子

SELECT id, 
       CAST(SUBSTRING(max(packed_col) FROM 2 FOR 6) AS float) as max_rev,
       SUBSTRING(max(packed_col) FROM 11) AS content_for_max_rev 
FROM  (SELECT id, 
       CAST(1000 + rev + .001 as CHAR) || '---' || CAST(content AS char) AS packed_col
       FROM yourtable
      ) 
GROUP BY id

打包开始时强制 rev 列为已知字符长度的数字,而不考虑 rev 的值,例如

  • 3.2 变为 1003.201
  • 57 变为 1057.001
  • 923.88 变为 1923.881

如果你做对了,两个数字的字符串比较应该产生与两个数字的数字比较相同的“最大”,并且很容易使用子字符串函数转换回原始数字(几乎在任何地方都以一种或另一种形式提供)。

3赞 inor 7/15/2013 #5

这个怎么样:

SELECT all_fields.*  
FROM (SELECT id, MAX(rev) FROM yourtable GROUP BY id) AS max_recs  
LEFT OUTER JOIN yourtable AS all_fields 
ON max_recs.id = all_fields.id
112赞 Vajk Hermecz 1/23/2014 #6

另一种解决方案是使用相关的子查询:

select yt.id, yt.rev, yt.contents
    from YourTable yt
    where rev = 
        (select max(rev) from YourTable st where yt.id=st.id)

在 (id,rev) 上设置索引几乎将子查询呈现为简单的查找......

以下是与 @AdrianCarneiro 的答案(子查询、左连接)中的解决方案的比较,基于 MySQL 测量值,InnoDB 表包含 ~100 万条记录,组大小为:1-3。

虽然对于全表扫描,子查询/左连接/相关时间相互关联为 6/8/9,但当涉及到直接查找或批处理 () 时,子查询比其他子查询慢得多(由于重新运行子查询)。但是,我无法在速度上区分 leftjoin 和相关解决方案。id in (1,2,3)

最后一点,由于 leftjoin 在组中创建 n*(n+1)/2 个联接,因此其性能会受到组大小的严重影响......

评论

1赞 Mark E. 9/20/2021
这种方法很难理解。我无法独立运行子查询,因为它引用了外部查询。子查询似乎一次返回一个值,但是,根据维基百科,“对于外部查询处理的每一行,子查询可能会被评估一次。在子查询中,yt.id 必须为行生成一个值?最终,对于每一行,子查询似乎都获得了该 ID 的最大转速。子查询在整个查询执行过程中的不同时间产生不同结果的想法似乎让我们很了解。
0赞 Vajk Hermecz 10/21/2021
@dolmen,请注意,内部选择是针对表中的每一行计算的。您假设它只计算一次,并且使用单个值。
0赞 dolmen 6/2/2022
@VajkHermecz 你是对的。我删除了我的评论。
0赞 siggi_pop 12/9/2022
不喜欢相关子查询
0赞 Marco Sulla 12/28/2022
这对于旧版本的MySql很有用(max()技巧选择了第一行,而不是最大值所在的行)
2赞 plavozont 1/29/2014 #7

此解决方案仅从 YourTable 中选择一个选项,因此速度更快。根据 sqlfiddle.com 上的测试,它仅适用于MySQL和SQLite(对于SQLite删除DESC)。也许可以调整它以在我不熟悉的其他语言上工作。

SELECT *
FROM ( SELECT *
       FROM ( SELECT 1 as id, 1 as rev, 'content1' as content
              UNION
              SELECT 2, 1, 'content2'
              UNION
              SELECT 1, 2, 'content3'
              UNION
              SELECT 1, 3, 'content4'
            ) as YourTable
       ORDER BY id, rev DESC
   ) as YourTable
GROUP BY id

评论

0赞 ma11hew28 3/14/2014
这似乎不适用于一般情况。而且,它在 PostgreSQL 中根本不起作用,返回:ERROR: column "your table.reb" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: SELECT *
0赞 plavozont 3/17/2014
对不起,我没有澄清它第一次在哪种语言上起作用。
6赞 KyleMit 5/30/2014 #8

不是 mySQL,但对于发现此问题并使用 SQL 的其他人,解决 个问题的另一种方法是在 MS SQL 中使用交叉应用

WITH DocIds AS (SELECT DISTINCT id FROM docs)

SELECT d2.id, d2.rev, d2.content
FROM DocIds d1
CROSS APPLY (
  SELECT Top 1 * FROM docs d
  WHERE d.id = d1.id
  ORDER BY rev DESC
) d2

下面是 SqlFiddle 中的一个示例

评论

0赞 nahab 2/15/2018
与其他方法相比非常慢 - 分组依据、窗口、不存在
4赞 Yuriy Nakonechnyy 7/3/2014 #9

由于这是关于这个问题的最热门问题,我也会在这里重新发布另一个答案:

看起来有更简单的方法可以做到这一点(但仅限于MySQL):

select *
from (select * from mytable order by id, rev desc ) x
group by id

请相信用户波西米亚人在这个问题中的回答为这个问题提供了如此简洁和优雅的答案。

编辑:虽然这个解决方案对很多人都有效,但从长远来看它可能不稳定,因为MySQL不保证GROUP BY语句会为不在GROUP BY列表中的列返回有意义的值。因此,使用此解决方案的风险由您自己承担!

评论

9赞 Jannes 10/10/2014
除了这是错误的,因为不能保证内部查询的顺序意味着什么,也不能保证 GROUP BY 总是采用遇到的第一行。至少在MySQL中,我会假设所有其他的。事实上,我假设MySQL会简单地忽略整个ORDER BY。任何将来的版本或配置更改都可能中断此查询。
0赞 Yuriy Nakonechnyy 10/10/2014
@Jannes这是有趣的评论:)我欢迎您回答我的问题,并提供证据:stackoverflow.com/questions/26301877/......
2赞 Yuriy Nakonechnyy 10/10/2014
@Jannes关于 GROUP BY 不保证采取遇到的第一行 - 您是完全正确的 - 发现这个问题 bugs.mysql.com/bug.php?id=71942 要求提供此类保证。现在将更新我的答案
1赞 Jannes 10/12/2014
我想我记得我从哪里得到 ORDER BY 被丢弃:MySQL 使用 UNION 执行此操作,如果您 ORDER BY 内部查询,它只是忽略:dev.mysql.com/doc/refman/5.0/en/union.html 说“如果 ORDER BY 在 SELECT 中没有 LIMIT 出现,它会被优化掉,因为它无论如何都不会起作用。我在这里没有看到有问题的查询的此类声明,但我不明白为什么它不能这样做。
17赞 Bulat 9/6/2014 #10

我喜欢使用基于-的解决方案来解决这个问题:NOT EXIST

SELECT 
  id, 
  rev
  -- you can select other columns here
FROM YourTable t
WHERE NOT EXISTS (
   SELECT * FROM YourTable t WHERE t.id = id AND rev > t.rev
)

这将选择组中具有最大值的所有记录,并允许您选择其他列。

评论

1赞 EGP 10/8/2014
是的,不存在,这通常是首选方式,而不是左连接。在旧版本的 SQL Server 中,它更快,尽管我认为现在它没有区别。我通常执行 SELECT 1 而不是 SELECT *,因为在以前的版本中它更快。
0赞 rjh 8/13/2021
至少在MySQL中,子查询会忽略其中的列。所以你在那里写什么并不重要SELECTEXISTS
2赞 akostadinov 3/25/2022
似乎是最简单、最独立的方法。可与分组和功能相媲美,但没有分组和功能。对于我的用例来说似乎也很快。简单性很重要,尤其是在使用 ORM 的情况下,这只是可以与其他条件链接的另一个条件,它不会像使用连接那样使查询结构复杂化。in
7赞 Jannes 10/10/2014 #11

注意:在MySQL 8 +天中,我可能不会再推荐这个了。好几年没用过了。

我几乎从未见过提到的第三个解决方案是特定于MySQL的,如下所示:

SELECT id, MAX(rev) AS rev
 , 0+SUBSTRING_INDEX(GROUP_CONCAT(numeric_content ORDER BY rev DESC), ',', 1) AS numeric_content
FROM t1
GROUP BY id

是的,它看起来很糟糕(转换为字符串并返回等),但根据我的经验,它通常比其他解决方案更快。也许这仅适用于我的用例,但我已经在具有数百万条记录和许多唯一 ID 的表上使用了它。也许是因为MySQL在优化其他解决方案方面非常糟糕(至少在我提出这个解决方案的5.0天里)。

一件重要的事情是,GROUP_CONCAT 对它可以构建的字符串有一个最大长度。您可能希望通过设置变量来提高此限制。请记住,如果您有大量行,这将是缩放的限制。group_concat_max_len

无论如何,如果您的内容字段已经是文本,则上述内容不能直接起作用。在这种情况下,您可能希望使用不同的分隔符,例如 \0 maybe。您也会更快地达到限制。group_concat_max_len

2赞 shay 1/7/2015 #12

这是一个很好的方法

使用以下代码:

with temp as  ( 
select count(field1) as summ , field1
from table_name
group by field1 )
select * from temp where summ = (select max(summ) from temp)
4赞 Marek Wysmułek 3/5/2015 #13

我会用这个:

select t.*
from test as t
join
   (select max(rev) as rev
    from test
    group by id) as o
on o.rev = t.rev

子查询 SELECT 可能不是太有效,但在 JOIN 子句中似乎是可用的。我不是优化查询的专家,但我尝试过 MySQL、PostgreSQL、FireBird,它确实效果很好。

您可以在多个联接和 WHERE 子句中使用此架构。这是我的工作示例(使用表“firmy”解决与您的问题相同的问题):

select *
from platnosci as p
join firmy as f
on p.id_rel_firmy = f.id_rel
join (select max(id_obj) as id_obj
      from firmy
      group by id_rel) as o
on o.id_obj = f.id_obj and p.od > '2014-03-01'

它是在有十几条记录的桌子上询问的,在真正不太强的机器上花费不到 0,01 秒。

我不会使用 IN 子句(正如上面某处提到的)。IN 用于短常量列表,而不是基于子查询构建的查询过滤器。这是因为 IN 中的子查询是针对每条扫描的记录执行的,这会使查询花费非常长的时间。

评论

1赞 Don Cheadle 1/11/2017
我认为使用该子查询作为 CTE 至少可以提高性能
0赞 Dmitry Grekov 8/10/2018
你好!对我来说,看起来您的第一个查询需要...... 最后(子查询应该为此返回)。不是吗?and o.id = t.idid
2赞 user5124980 7/17/2015 #14

我喜欢通过按某些列对记录进行排名来做到这一点。在本例中,按 分组的排名值。排名越高的人排名越低。因此,最高排名为 1。revidrevrev

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

评论

0赞 user5124980 7/17/2015
仅在MySQL中尝试过方法。Oracle 也有类似的功能来对记录进行排名。想法也应该有效。
1赞 philipxy 9/22/2018
在MySQL中,在select语句中读取和写入变量是未定义的,尽管特定版本恰好给出了您可能期望的某些涉及大小写表达式的语法的答案。
3赞 seahawk 9/4/2015 #15

如果 select 语句中有许多字段,并且希望通过优化的代码获得所有这些字段的最新值:

select * from
(select * from table_name
order by id,rev desc) temp
group by id 

评论

0赞 Rick James 5/17/2017
这适用于小型表,但需要对整个数据集进行 6 次传递,因此对于大型表来说速度不快。
0赞 Mike Viens 6/2/2018
这是我需要的查询,因为还涉及其他列。
1赞 blokeish 12/11/2015 #16

以相反的顺序对 rev 字段进行排序,然后按 id 分组,给出每个分组的第一行,即具有最高 rev 值的那行。

SELECT * FROM (SELECT * FROM table1 ORDER BY id, rev DESC) X GROUP BY X.id;

http://sqlfiddle.com/ 中测试,使用以下数据

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')
;

这在 MySql 5.5 和 5.6 中给出了以下结果

id  rev content
1   2   One-Two
2   2   Two-Two
3   3   Three-Two

评论

0赞 Rick James 4/2/2017
这种技术曾经有效,但现在不再有效。查看 mariadb.com/kb/en/mariadb/...
1赞 blokeish 4/3/2017
原始问题标签是“mysql”,我已经非常清楚地指出,我的解决方案在 sqlfiddle.com 年使用 Mysql 5.5 和 5.6 进行了测试。我已经提供了独立验证解决方案的所有步骤。我没有做出任何虚假的声明,即我的解决方案适用于Mariadb。Mariadb 不是 Mysql,它只是 Mysql 的直接替代品,由 2 家不同的公司拥有。您的评论将帮助任何试图在 Mariadb 中实现它的人,但我的帖子绝不值得投反对票,因为它清楚地回答了提出的问题。
1赞 Rick James 4/3/2017
是的,它适用于旧版本。我过去也使用过这种技术,只是在它停止工作时被烧毁。此外,MySQL(在5.7中?)也将忽略子查询中的。由于很多人会阅读你的答案,我试图引导他们远离一种会破坏他们未来的技术。(我没有给你 -1 票。ORDER BY
1赞 philipxy 9/22/2018
测试什么也证明不了。子查询中的 ORDER BY 除了同一子查询中的 LIMIT 外,没有其他保证效果。即使保留了秩序,GROUP BY也不会保留它。即使保留了它,也指定了依赖于禁用ONLY_FULL_GROUP_BY的非标准 GROUP BY 来返回非分组列的组中的一行,但不一定是第一行。所以你的查询是不正确的。
240赞 topchef 8/9/2016 #17

令我大吃一惊的是,没有提供SQL窗口函数解决方案的答案:

SELECT a.id, a.rev, a.contents
  FROM (SELECT id, rev, contents,
               ROW_NUMBER() OVER (PARTITION BY id ORDER BY rev DESC) ranked_order
          FROM YourTable) a
 WHERE a.ranked_order = 1 

在 SQL 标准 ANSI/ISO 标准 SQL:2003 中添加,后来通过 ANSI/ISO 标准 SQL:2008 进行扩展,窗口(或窗口)功能现在可供所有主要供应商使用。还有更多类型的排名函数可用于处理平局问题:.RANK, DENSE_RANK, PERSENT_RANK

评论

6赞 topchef 1/11/2017
直觉是一件棘手的事情。我发现它比其他答案更直观,因为它构建了明确的数据结构来回答这个问题。但是,再一次,直觉是偏见的另一面......
13赞 Rick James 4/2/2017
这可能适用于 MariaDB 10.2 和 MySQL 8.0.2,但在此之前无效。
3赞 Mark Andersen 3/31/2021
由于简单性,应首选窗口函数的方法。
4赞 Paramvir Singh Karwal 2/1/2022
与相关查询(性能杀手)或其他聚合函数相比,这是更有效的方法。现在应将其标记为已接受的答案。
2赞 deweydb 7/1/2022
我认为您不能用作代码第 3 行的字段名称。至少在MySQL 8.0.29中没有。 是一个保留词,你必须使用其他东西,比如或任何你想要的东西。rankrankranked_order
46赞 HoldOffHunger 9/14/2016 #18

唯一标识符?是的!唯一标识符!

开发 MySQL 数据库的最佳方法之一是让每个 id AUTOINCREMENT (Source MySQL.com)。这允许各种优点,太多了,这里就不一一赘述了。这个问题的问题在于它的示例具有重复的 ID。这忽视了唯一标识符的这些巨大优势,同时也让那些已经熟悉这一点的人感到困惑。

最干净的解决方案

DB 小提琴

默认情况下,较新版本的MySQL是启用的,在这种情况下,这里的许多解决方案在测试中将失败ONLY_FULL_GROUP_BY

即便如此,我们也可以简单地选择 someuniquefieldwhateverotherfieldtoselect , *somethirdfield 等,而不必担心理解结果或查询的工作原理:DISTINCTMAX()()

SELECT DISTINCT t1.id, MAX(t1.rev), MAX(t2.content)
FROM Table1 AS t1
JOIN Table1 AS t2 ON t2.id = t1.id AND t2.rev = (
    SELECT MAX(rev) FROM Table1 t3 WHERE t3.id = t1.id
)
GROUP BY t1.id;
  • SELECT DISTINCT Table1.id, max(Table1.rev), max(Table2.content):返回某个字段,某个其他字段,最后一个是多余的,因为我知道它只是一行,但它是查询所必需的。DISTINCTMAX()MAX()
  • FROM Employee:搜索到的表。
  • JOIN Table1 AS Table2 ON Table2.rev = Table1.rev:将第二个表连接到第一个表上,因为我们需要获取 max(table1.rev) 的注释。
  • GROUP BY Table1.id:强制将每个员工的排名靠前的“工资”行作为返回结果。

请注意,由于“内容”是“...”在 OP 的问题中,没有办法测试这是否有效。所以,我把它改成了“..a", "..b“,因此,我们现在实际上可以看到结果是正确的:

id  max(Table1.rev) max(Table2.content)
1   3   ..d
2   1   ..b

为什么是干净的? 、 等,都很好地利用了 MySQL 索引。这会更快。或者,如果您具有索引,并将其与查看所有行的查询进行比较,则速度会快得多。DISTINCT()MAX()

原创解决方案

禁用后,我们仍然可以使用,但是我们只在 Salary 上使用它,而不是 id:ONLY_FULL_GROUP_BYGROUP BY

SELECT *
FROM
    (SELECT *
    FROM Employee
    ORDER BY Salary DESC)
AS employeesub
GROUP BY employeesub.Salary;
  • SELECT *:返回所有字段。
  • FROM Employee:搜索到的表。
  • (SELECT *...)subquery :返回所有人员,按 Salary 排序。
  • GROUP BY employeesub.Salary:强制将每个员工的排名靠前的“工资”行作为返回结果。

Unique-Row 解决方案

请注意关系数据库的定义:“表中的每一行都有其自己的唯一键。这意味着,在问题的示例中,id 必须是唯一的,在这种情况下,我们可以这样做:

SELECT *
FROM Employee
WHERE Employee.id = 12345
ORDER BY Employee.Salary DESC
LIMIT 1

希望这是一个解决方案,可以解决问题,并帮助每个人更好地了解数据库中发生的事情。

2赞 Abdul Samad 6/20/2017 #19

这是另一个解决方案,希望它能帮助某人

Select a.id , a.rev, a.content from Table1 a
inner join 
(SELECT id, max(rev) rev FROM Table1 GROUP BY id) x on x.id =a.id and x.rev =a.rev
2赞 qaisjp 7/14/2017 #20

这些答案都对我不起作用。

这就是对我有用的东西。

with score as (select max(score_up) from history)
select history.* from score, history where history.score_up = score.max
9赞 guru008 7/31/2017 #21
SELECT *
FROM Employee
where Employee.Salary in (select max(salary) from Employee group by Employe_id)
ORDER BY Employee.Salary
2赞 Cesar 10/17/2017 #22

这是仅使用具有该字段最大值的字段检索记录的另一种解决方案。这适用于 SQL400,这是我工作的平台。在此示例中,字段 FIELD5 中具有最大值的记录将通过以下 SQL 语句进行检索。

SELECT A.KEYFIELD1, A.KEYFIELD2, A.FIELD3, A.FIELD4, A.FIELD5
  FROM MYFILE A
 WHERE RRN(A) IN
   (SELECT RRN(B) 
      FROM MYFILE B
     WHERE B.KEYFIELD1 = A.KEYFIELD1 AND B.KEYFIELD2 = A.KEYFIELD2
     ORDER BY B.FIELD5 DESC
     FETCH FIRST ROW ONLY)
0赞 Richard Ball 1/5/2018 #23

我用下面的方法解决了我自己的问题。我首先创建了一个临时表,并插入了每个唯一 ID 的最大转速值。

CREATE TABLE #temp1
(
    id varchar(20)
    , rev int
)
INSERT INTO #temp1
SELECT a.id, MAX(a.rev) as rev
FROM 
    (
        SELECT id, content, SUM(rev) as rev
        FROM YourTable
        GROUP BY id, content
    ) as a 
GROUP BY a.id
ORDER BY a.id

然后,我将这些最大值 (#temp1) 连接到所有可能的 id/内容组合。通过这样做,我自然而然地过滤掉了非最大 id/content 组合,并为每个组合留下了唯一的最大转速值。

SELECT a.id, a.rev, content
FROM #temp1 as a
LEFT JOIN
    (
        SELECT id, content, SUM(rev) as rev
        FROM YourTable
        GROUP BY id, content
    ) as b on a.id = b.id and a.rev = b.rev
GROUP BY a.id, a.rev, b.content
ORDER BY a.id
24赞 schlebe 2/20/2018 #24

完成这项工作的另一种方法是在 OVER PARTITION 子句中使用分析函数MAX()

SELECT t.*
  FROM
    (
    SELECT id
          ,rev
          ,contents
          ,MAX(rev) OVER (PARTITION BY id) as max_rev
      FROM YourTable
    ) t
  WHERE t.rev = t.max_rev 

本文中已经记录的另一个 OVER PARTITION 解决方案是ROW_NUMBER()

SELECT t.*
  FROM
    (
    SELECT id
          ,rev
          ,contents
          ,ROW_NUMBER() OVER (PARTITION BY id ORDER BY rev DESC) rank
      FROM YourTable
    ) t
  WHERE t.rank = 1 

这 2 个 SELECT 在 Oracle 10g 上运行良好。

MAX() 解决方案的运行速度肯定比解决方案快,因为复杂性是最小的,而复杂性是最小的,其中表示表中的记录数!ROW_NUMBER()MAX()O(n)ROW_NUMBER()O(n.log(n))n

评论

1赞 sanpat 10/30/2020
第一个查询是完美的,大多数 SO 帖子都缺乏这种讨论。当我们获得更多列时,它的性能高效且有用。大多数其他解决方案是关于获取一列的最大值,而不是当单个组中有 10 行时具有多列的多行。呵呵。
1赞 Ali Sarchami 12/27/2020
与所有其他解决方案相比,性能最佳。对于我的用例,这几乎是数千个分区和数千万条记录的 9 倍。
0赞 wizzard0 6/9/2021
在 MySQL 8 和 SQLite 中也可以工作,并且工作速度很快。是的,同意 MAX() 是最佳选择。
1赞 zovio 9/17/2018 #25

当您将 和 合并为一个值,然后将其拆分回原始值时,可以在没有联接的情况下进行选择:revidmaxRevIdMAX()

SELECT maxRevId & ((1 << 32) - 1) as id, maxRevId >> 32 AS rev
FROM (SELECT MAX(((rev << 32) | id)) AS maxRevId
      FROM YourTable
      GROUP BY id) x;

当存在复杂联接而不是单个表时,这尤其快。使用传统方法,复杂连接将执行两次。

上面的组合很简单,位函数为 when 和 are(32 位),组合值适合 (64 位)。当 & 大于 32 位值或由多列组成时,您需要将该值组合成一个二进制值,并带有合适的填充。revidINT UNSIGNEDBIGINT UNSIGNEDidrevMAX()

6赞 Abhishek Rana 12/29/2018 #26

我想,你想要这个吗?

select * from docs where (id, rev) IN (select id, max(rev) as rev from docs group by id order by id)  

SQL Fiddle : 点击这里

评论

1赞 jdhao 9/30/2022
stackoverflow.com/a/7745679/6064933 的重复,没有信息增益。
1赞 Ian A McElhenny 2/22/2019 #27

解释

这不是纯粹的 SQL。这将使用 SQLAlchemy ORM。

我来这里寻求 SQLAlchemy 的帮助,所以我将用 python/SQLAlchemy 版本复制 Adrian Carneiro 的答案,特别是外部连接部分。

此查询回答了以下问题:

“您能否将这组记录(基于相同 ID)中具有最高版本号的记录归还给我”。

这允许我复制记录、更新记录、增加其版本号,并以可以显示随时间变化的方式获得旧版本的副本。

法典

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 数据库上测试。