SQL仅选择列上具有最大值的行[重复]
Posted
技术标签:
【中文标题】SQL仅选择列上具有最大值的行[重复]【英文标题】:SQL select only rows with max value on a column [duplicate] 【发布时间】:2021-04-25 18:39:02 【问题描述】:我有这张文件表(这里是简化版):
id | rev | content |
---|---|---|
1 | 1 | ... |
2 | 1 | ... |
1 | 2 | ... |
1 | 3 | ... |
如何为每个 id 选择一行并且只选择最大的 rev?
使用上述数据,结果应包含两行:[1, 3, ...]
和[2, 1, ..]
。我正在使用 MySQL。
目前我在while
循环中使用检查来检测和覆盖结果集中的旧转速。但这是实现结果的唯一方法吗?没有SQL解决方案吗?
【问题讨论】:
行需要对应的content
字段吗?
是的,这不会造成任何问题,我已经剪掉了很多列,我会添加回来。
@MarkByers 我已经编辑了我的答案以符合 OP 的需求。既然在这,我决定就greatest-n-per-group这个话题写一个更全面的答案。
这是常见的greatest-n-per-group 问题,经过充分测试和optimized solutions。我更喜欢left join solution by Bill Karwin(original post)。请注意,可以在最官方的资源之一 mysql 手册 中找到许多解决这个常见问题的方法!见Examples of Common Queries :: The Rows Holding the Group-wise Maximum of a Certain Column。
重复Retrieving the last record in each group
【参考方案1】:
乍一看...
您只需要一个带有MAX
聚合函数的GROUP BY
子句:
SELECT id, MAX(rev)
FROM YourTable
GROUP BY id
从来没有那么简单,不是吗?
我刚刚注意到您还需要 content
列。
这是 SQL 中一个非常常见的问题:在每个组标识符的列中查找具有某个最大值的行的整个数据。在我的职业生涯中,我听到了很多。实际上,这是我在当前工作的技术面试中回答的问题之一。
实际上,Stack Overflow 社区创建了一个标签来处理此类问题非常普遍:greatest-n-per-group。
基本上,您有两种方法可以解决该问题:
加入简单的group-identifier, max-value-in-group
子查询
在这种方法中,您首先在子查询中找到group-identifier, max-value-in-group
(上面已经解决)。然后你将你的表加入到子查询中,group-identifier
和 max-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
左连接,调整连接条件和过滤器
在这种方法中,您离开了与自身连接的表。平等进入group-identifier
。然后,两个聪明的动作:
-
第二个连接条件是左侧值小于右侧值
当您执行第 1 步时,实际具有最大值的行将在右侧显示
NULL
(它是 LEFT JOIN
,记得吗?)。然后,我们过滤连接的结果,只显示右侧为NULL
的行。
所以你最终得到:
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;
结论
这两种方法带来完全相同的结果。
如果您有两行 max-value-in-group
对应 group-identifier
,则这两行都将出现在两种方法的结果中。
这两种方法都与 SQL ANSI 兼容,因此,无论其“风格”如何,都可以与您最喜欢的 RDBMS 一起使用。
这两种方法对性能也很友好,但是您的使用范围可能会有所不同(RDBMS、数据库结构、索引等)。因此,当您选择一种方法而不是另一种方法时,基准测试。并确保您选择对您最有意义的那个。
【讨论】:
这是一个非常糟糕的主意,因为您想要最大化的字段可能是双精度数,并且比较双精度数是否相等是不确定的。我认为只有 O(n^2) 算法在这里有效。 @Adriano 如果有一个额外的列user_id
并且您想将结果限制在 user_id
,这将如何工作?我想这个过滤器应该在很早的时候发生,以避免它把不相关的user_id
s 的东西连接在一起,这些东西会在以后被抛弃?
我不确定这两种方法是否会“带来完全相同的结果”:我认为第二种方法将保留 rev
字段为 NULL 的记录(它们在加入),但第一种方法不会保留它们(它们的转速不是最大值,所以它们没有被选中)。
另一种方法是使用窗口函数。它们似乎提供了更好的性能。我会这样做:SELECT DISTINCT id, MAX(rev) OVER (PARTITION BY id), FIRST_VALUE(content) OVER (PARTITION BY id ORDER BY rev DESC) FROM YourTable
@mk3009hppw:比较双打是否相等是完全确定性的,尽管认为它不是某种原因是一个常见的误解。人们通常的意思是(如果他们不只是模仿他们从其他地方听到的东西)是不精确的浮点计算(可能像 0.1 + 0.2 一样简单)可能不会完全返回“预期”结果(0.3)由于舍入,否则comparing numeric types with different precision 可能会出现意外行为。但这些都不会在这里发生。【参考方案2】:
我的偏好是使用尽可能少的代码...
您可以使用IN
试试这个:
SELECT *
FROM t1 WHERE (id,rev) IN
( SELECT id, MAX(rev)
FROM t1
GROUP BY id
)
在我看来,它不那么复杂...更易于阅读和维护。
【讨论】:
好奇——我们可以在哪个数据库引擎中使用这种类型的 WHERE 子句?这在 SQL Server 中不受支持。 oracle & mysql(不确定其他数据库抱歉) 也适用于 PostgreSQL。 确认在 DB2 中工作 不适用于 SQLite。【参考方案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)
【讨论】:
无连接的不会剪吗? 如果他们工作,那么他们也很好。 这似乎是最快的(有适当的索引)。 另一个 ON 上没有孩子让我着迷!【参考方案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如果你做得对,两个数字的字符串比较应该产生与两个数字的数字比较相同的“最大值”,并且使用 substring 函数很容易转换回原始数字(它有一种形式或几乎无处不在)。
【讨论】:
【参考方案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
【讨论】:
【参考方案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 的答案(子查询,leftjoin)中的解决方案的比较,基于 MySQL 测量,InnoDB 表有约 100 万条记录,组大小为:1-3。
虽然对于全表扫描,子查询/leftjoin/相关时间相互关联为 6/8/9,但对于直接查找或批处理 (id in (1,2,3)
),子查询比其他查询慢得多(由于重新运行子查询)。但是,我无法在速度上区分 leftjoin 和相关解决方案。
最后一点,由于 leftjoin 在组中创建 n*(n+1)/2 个连接,因此其性能可能会受到组大小的严重影响...
【讨论】:
这种方法很难理解。我不能独立运行子查询,因为它引用了外部查询。看起来子查询一次返回一个值,但是,根据***,“子查询可以为外部查询处理的每一行评估一次。”在子查询中,yt.id 必须为该行生成一个值吗?最终,对于每一行,子查询似乎都获得了该 ID 的最大转速。子查询在整个查询执行的不同时间产生不同结果的想法似乎让我们深入了解。 这不起作用。rev
相对于id
:每个id
都有其rev
的历史。根据您的提议,您为所有id
选择相同的rev
编号,而不是为每个id
选择最大的编号。
@dolmen,请注意,内部选择会针对表中的每一行进行评估。您假设它只评估一次,并且使用单个值。
这个!正是没有 joins/windows/group by 的情况下解决了我的问题【参考方案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
【讨论】:
这似乎不适用于一般情况。而且,它在 PostgreSQL 中根本不起作用,返回:ERROR: column "your table.reb" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: SELECT *
对不起,我第一次没有说明它在哪种语言上起作用。【参考方案8】:
不是 mySQL,但对于发现此问题并使用 SQL 的其他人来说,解决 greatest-n-per-group 问题的另一种方法是在 MS SQL 中使用 Cross Apply
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
Here's an example in SqlFiddle
【讨论】:
与其他方法相比非常慢 - 分组,窗口,不存在【参考方案9】:由于这是关于这个问题的最受欢迎的问题,我也会在这里重新发布另一个答案:
看起来有更简单的方法可以做到这一点(但仅在 MySQL 中):
select *
from (select * from mytable order by id, rev desc ) x
group by id
请注明this question 中用户 Bohemian 的回答,因为他为这个问题提供了如此简洁和优雅的答案。
编辑:虽然这个解决方案适用于许多人,但从长远来看它可能不稳定,因为 MySQL 不保证 GROUP BY 语句将为不在 GROUP BY 列表中的列返回有意义的值.因此,使用此解决方案需要您自担风险!
【讨论】:
除非它是错误的,因为不能保证内部查询的顺序意味着什么,也不能保证 GROUP BY 总是取第一个遇到的行。至少在 MySQL 中,我会假设所有其他人。事实上,我假设 MySQL 会简单地忽略整个 ORDER BY。任何未来版本或配置更改都可能会破坏此查询。 @Jannes 这句话很有趣 :) 欢迎您回答我的问题并提供证明:***.com/questions/26301877/… @Jannes 关于 GROUP BY 不能保证采取第一个遇到的行 - 你是完全正确的 - 发现这个问题 bugs.mysql.com/bug.php?id=71942 要求提供这样的保证。现在将更新我的答案 我想我记得我从哪里得到了 ORDER BY 被丢弃:如果你 ORDER BY 内部查询,MySQL 会使用 UNIONs,它只是忽略:dev.mysql.com/doc/refman/5.0/en/union.html 说“如果 ORDER BY 出现没有限制在 SELECT 中,它被优化掉了,因为它无论如何都没有效果。”我在这里没有看到有问题的查询的这样的声明,但我不明白为什么它不能这样做。【参考方案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
)
这将选择组内具有最大值的所有记录,并允许您选择其他列。
【讨论】:
是的,不存在这样的通常是首选方式,而不是左连接。在旧版本的 SQL Server 中它更快,虽然我认为现在它没有区别。我通常使用 SELECT 1 而不是 SELECT *,因为在以前的版本中它更快。 至少在 MySQL 中,SELECT
中的列对于 EXISTS
子查询会被忽略。所以你在那里写什么并不重要【参考方案11】:
我几乎从未见过提到的第三种解决方案是特定于 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 。您还将更快地遇到group_concat_max_len
限制。
【讨论】:
【参考方案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)
【讨论】:
【参考方案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 中的子查询是针对每个扫描的记录执行的,这会使查询花费很长时间。
【讨论】:
我认为使用该子查询作为 CTE 至少可以提高性能 嗨!对我来说,看起来您的第一个查询最终需要...and o.id = t.id
(并且子查询应该为此返回id
)。不是吗?【参考方案14】:
我喜欢通过按某个列对记录进行排名来做到这一点。在这种情况下,对按id
分组的rev
值进行排名。 rev
较高的那些排名较低。所以最高的rev
排名第一。
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 对记录排名有类似的功能。想法也应该奏效。 在 select 语句中读取和写入变量在 MySQL 中是未定义的,尽管特定版本恰好给出了您可能期望的某些涉及 case 表达式的语法的答案。【参考方案15】:如果您在 select 语句中有许多字段,并且您希望通过优化代码为所有这些字段提供最新值:
select * from
(select * from table_name
order by id,rev desc) temp
group by id
【讨论】:
这适用于小型表,但需要 6 次遍历整个数据集,因此对于大型表来说并不快。 这是我需要的查询,因为还涉及其他列。【参考方案16】:将 rev 字段以相反的顺序排序,然后按 id 分组,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
【讨论】:
这种技术曾经有效,但不再有效。见mariadb.com/kb/en/mariadb/… 原始问题标签是“mysql”,我已经非常清楚地表明我的解决方案在 sqlfiddle.com 中使用 Mysql 5.5 和 5.6 进行了测试。我已经提供了独立验证解决方案的所有步骤。我没有做出任何虚假声称我的解决方案适用于 Mariadb。 Mariadb 不是 Mysql,它只是 Mysql 的替代品,由 2 家不同的公司拥有。您的评论将帮助任何试图在 Mariadb 中实施它的人,但我的帖子绝不应该投反对票,因为它清楚地回答了所提出的问题。 是的,它适用于旧版本。我过去曾使用过这种技术,只是在它停止工作时被烧毁。 MySQL(在 5.7 中?)也将忽略子查询中的ORDER BY
。由于很多人会阅读您的答案,因此我试图引导他们远离一种会在他们的未来打破的技术。 (而且我没有给你-1 票。)
测试证明什么。子查询中的 ORDER BY 没有保证效果,除了同一子查询中的 LIMIT。即使保留了顺序, GROUP BY 也不会保留它。即使它被保留,依赖于禁用的 ONLY_FULL_GROUP_BY 的非标准 GROUP BY 被指定为在组中为非分组列返回 some 行,但不一定是第一个。所以你的查询不正确。【参考方案17】:
我惊呆了,没有答案提供 SQL 窗口函数解决方案:
SELECT a.id, a.rev, a.contents
FROM (SELECT id, rev, contents,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY rev DESC) rank
FROM YourTable) a
WHERE a.rank = 1
在 SQL 标准 ANSI/ISO 标准 SQL:2003 中添加,后来通过 ANSI/ISO 标准 SQL:2008 进行了扩展,现在所有主要供应商都可以使用窗口(或窗口)函数。有更多类型的排名函数可用于处理平局问题:RANK, DENSE_RANK, PERSENT_RANK
。
【讨论】:
直觉是一件棘手的事情。我发现它比其他答案更直观,因为它构建了回答问题的明确数据结构。但是,同样,直觉是偏见的另一面…… 这可能适用于 MariaDB 10.2 和 MySQL 8.0.2,但之前不行。 由于简单,应该首选窗口函数的方法。 是的,窗口函数似乎是一种更好的方法。至少它有更好的性能。不过,我会使用 MAX 和 FIRST_VALUE 函数:SELECT DISTINCT id, MAX(rev) OVER (PARTITION BY id), FIRST_VALUE(content) OVER (PARTITION BY id ORDER BY rev DESC) FROM YourTable【参考方案18】:唯一标识符?是的!唯一标识符!
开发 MySQL 数据库的最佳方法之一是to have each id
AUTOINCREMENT
(来源 MySQL.com)。这可以带来多种优势,这里不一一赘述。该问题的问题在于其示例具有重复的 ID。这无视了唯一标识符的这些巨大优势,同时也让那些已经熟悉这一点的人感到困惑。
最干净的解决方案
DB Fiddle
较新版本的 MySQL 默认启用ONLY_FULL_GROUP_BY
,这里的许多解决方案将失败在这种情况下进行测试。
即便如此,我们也可以简单地选择 DISTINCT
someuniquefield、MAX(
whateverotherfieldtoselect )
、(
*somethirdfield )
等,以及不用担心理解结果或查询是如何工作的:
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)
: return DISTINCT
somefield, MAX()
some otherfield, 最后一个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
为什么是干净的? DISTINCT()
、MAX()
等等,都很好地利用了 MySQL 索引。这会更快。或者,如果您有索引并将其与查看所有行的查询进行比较,它会更快。
原方案
禁用ONLY_FULL_GROUP_BY
,我们仍然可以使用GROUP BY
,但是我们只在工资上使用它,而不是在id上:
SELECT *
FROM
(SELECT *
FROM Employee
ORDER BY Salary DESC)
AS employeesub
GROUP BY employeesub.Salary;
SELECT *
:返回所有字段。
FROM Employee
: 搜索表。
(SELECT *...)
subquery : 返回所有人,按薪水排序。
GROUP BY employeesub.Salary
: 强制将每个员工的薪水排在最前面的行作为返回结果。
唯一行解决方案
注意Definition of a Relational Database:“表中的每一行都有自己的唯一键。”这意味着,在问题的示例中,id 必须是唯一的,在这种情况下,我们可以这样做:
SELECT *
FROM Employee
WHERE Employee.id = 12345
ORDER BY Employee.Salary DESC
LIMIT 1
希望这是一个解决问题的解决方案,并帮助每个人更好地了解数据库中正在发生的事情。
【讨论】:
【参考方案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
【讨论】:
【参考方案20】:这些答案都不适合我。
这对我有用。
with score as (select max(score_up) from history)
select history.* from score, history where history.score_up = score.max
【讨论】:
【参考方案21】:SELECT *
FROM Employee
where Employee.Salary in (select max(salary) from Employee group by Employe_id)
ORDER BY Employee.Salary
【讨论】:
【参考方案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)
【讨论】:
【参考方案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/content 组合中。通过这样做,我自然会过滤掉非最大 id/content 组合,并留下每个唯一的最大 rev 值。
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】:完成这项工作的另一种方法是在 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
本文中已经记录的另一个ROW_NUMBER()
OVER PARTITION 解决方案是
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
表示表中的记录数!
【讨论】:
第一个查询是完美的,大多数 SO 帖子都缺乏讨论。当我们获得更多列时,它的性能高效且有用。当单个组中每个组有 10 行时,其他大多数解决方案都是关于获得一列的最大值,而不是多行多列。谢谢。 这通常是我的首选方法 与所有其他解决方案相比性能最佳。对于我的用例,这几乎快 9 倍,有数千个分区和数千万条记录。 在 MySQL 8 和 SQLite 中也可以工作,而且工作速度很快。也是的,同意 MAX() 是最好的选择。【参考方案25】:当您将rev
和id
组合成一个maxRevId
的MAX()
值,然后将其拆分回原始值时,您可以在没有连接的情况下进行选择:
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;
当存在复杂连接而不是单个表时,这尤其快。使用传统方法,复杂的连接将执行两次。
当rev
和id
为INT UNSIGNED
(32 位)并且组合值适合BIGINT UNSIGNED
(64 位)时,上述组合很简单。当id
& rev
大于 32 位值或由多个列组成时,您需要将值组合成例如带有适合MAX()
填充的二进制值。
【讨论】:
【参考方案26】:我想,你想要这个?
select * from docs where (id, rev) IN (select id, max(rev) as rev from docs group by id order by id)
SQL 小提琴: Check here
【讨论】:
【参考方案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 数据库上测试。
【讨论】:
以上是关于SQL仅选择列上具有最大值的行[重复]的主要内容,如果未能解决你的问题,请参考以下文章