40

我有下表称为问题:

ID | asker 
1  | Bob
2  | Bob
3  | Marley

我只想选择每个提问者一次,如果有多个同名提问者,请选择 ID 最高的一个。所以,预期的结果:

ID | asker 
3  | Marley
2  | Bob

我使用以下查询:

SELECT * FROM questions GROUP by questions.asker ORDER by questions.id DESC

我得到以下结果:

ID | asker 
3  | Marley
1  | Bob

它选择它遇到的第一个“鲍勃”而不是最后一个。

4

7 回答 7

62

如果你想要id每个的最后一个asker,那么你应该使用一个聚合函数:

SELECT max(id) as id, 
   asker
FROM questions 
GROUP by asker 
ORDER by id DESC

您得到不寻常结果的原因是因为 MySQL 使用了一个扩展GROUP BY,允许选择列表中的项目不聚合并且不包含在 GROUP BY 子句中。然而,这可能会导致意外的结果,因为 MySQL 可以选择返回的值。(参见MySQL 对 GROUP BY 的扩展

来自 MySQL 文档:

MySQL 扩展了 GROUP BY 的使用,以便选择列表可以引用未在 GROUP BY 子句中命名的非聚合列。...您可以使用此功能通过避免不必要的列排序和分组来获得更好的性能。但是,这主要在每个未在 GROUP BY 中命名的非聚合列中的所有值对于每个组都相同时很有用。服务器可以从每个组中自由选择任何值,因此除非它们相同,否则选择的值是不确定的。此外,从每个组中选择值不会受到添加 ORDER BY 子句的影响。在选择了值之后对结果集进行排序,并且 ORDER BY 不会影响服务器选择的值。

现在,如果您需要从表中返回其他列,但由于可能获得的结果不一致而不想将它们添加到表中GROUP BY,那么您可以使用子查询来执行此操作。(演示

select 
  q.Id,
  q.asker,
  q.other -- add other columns here
from questions q
inner join
(
  -- get your values from the group by
  SELECT max(id) as id, 
    asker
  FROM questions 
  GROUP by asker 
) m
  on q.id = m.id
order by q.id desc
于 2013-03-13T16:01:24.937 回答
20

通常 MySQL 只允许按升序记录分组。所以我们可以在分组之前对记录进行排序。

选择 *
从 (
  选择 *
  来自问题
  ORDER BY id DESC
) 作为问题
GROUP BY questions.asker
于 2014-06-13T10:38:51.807 回答
4

需要使用GROUP BY和对记录进行分组,MAX()以获得每个 的最大 ID asker

SELECT  asker, MAX(ID) ID
FROM    TableName
GROUP   BY asker

输出

╔════════╦════╗
║ ASKER  ║ ID ║
╠════════╬════╣
║ Bob    ║  2 ║
║ Marley ║  3 ║
╚════════╩════╝
于 2013-03-13T16:01:20.103 回答
4

我写这个答案是因为@Taryn 在接受的答案中的第一个/更短的替代方案只有在您只选择 GROUP BY 和 MAX 中使用的列时才有效。用户提出的问题是选择表中的所有列(他使用了 SELECT *)。因此,当您向表中添加另一个第三列时,查询结果中的该列值将不正确。您将从不同的表行中获得混合值。@Taryn 的第二个/更长的替代方案(使用内部联接和子查询)有效,但查询是无用的复杂,在我的用例中比我下面的简单替代方案慢 5 倍。


考虑表questions

id | asker 
-----------
1  | Bob
2  | Bob
3  | Marley

查询SELECT max(id) as id, asker FROM questions GROUP BY asker ORDER BY id DESC返回预期

id | asker 
-----------
3  | Marley
2  | Bob

现在考虑另一个表questions

id | asker  | other
-------------------
1  | Bob    | 1st
2  | Bob    | 2nd
3  | Marley | 3rd

查询SELECT max(id) as id, asker, other FROM questions GROUP BY asker ORDER BY id DESC返回意外

id | asker  | other
-------------------
3  | Marley | 3rd
2  | Bob    | 1st

...请注意,other第二行结果的值不正确,因为id=2来自表格的第二行但other=1st来自表格的第一行!这就是许多用户在评论 Taryn 的答案时报告该解决方案不起作用的方式。


选择其他列时可能的简单解决方案是使用GROUP BY+ DESC

SELECT id, asker, other FROM questions GROUP BY asker DESC

id | asker  | other
-------------------
3  | Marley | 3rd
2  | Bob    | 2nd

(见演示:https ://www.db-fiddle.com/f/esww483qFQXbXzJmkHZ8VT/10 )

...但是这个简单的解决方案有一些限制:

  • 表必须是 InnoDB(我认为这不是问题,因为您将获得更好的性能,而且 MySQL >= 5.5.5 默认/首选存储引擎已从 MyISAM 更改为 InnoDB)
  • 您必须为 GROUP BY 中使用的列创建索引 - 所以asker在这种情况下(我认为这不是问题,因为索引适用于这种情况,因此您将获得更好的性能。GROUP BY 通常需要创建 tmp 表但是当索引可用的 tmp 表不会被创建,这样更快)
  • 对于 MySQL 5.7 和 8.0,需要禁用 SQL 模式 ONLY_FULL_GROUP_BY(例如SET SESSION sql_mode = '';)或ANY_VALUE()在未聚合的选定列上使用以避免错误 ER_WRONG_FIELD_WITH_GROUP。
  • 不幸的是,自 MySQL 8.0 https://dev.mysql.com/worklog/task/?id=8693以来,MySQL 开发人员在 GROUP BY 中删除了对 ASC/DESC 的支持,但幸运的是还有其他选择GROUP BY col1 ORDER BY col1 ASC/DESC

SELECT id, asker, other FROM questions GROUP BY asker ORDER BY asker DESC

id | asker  | other
-------------------
3  | Marley | 3rd
2  | Bob    | 2nd

(见演示:https ://www.db-fiddle.com/f/esww483qFQXbXzJmkHZ8VT/11 )

...结果与上面相同GROUP BY ... DESC(不要忘记使用 InnoDB 并创建索引)。

于 2019-04-01T10:29:09.153 回答
3

其他人关于使用 MAX(ID) 来获得你想要的结果是正确的。如果您想知道为什么您的查询不起作用,那是因为ORDER BY发生GROUP BY.

于 2013-03-14T04:08:17.383 回答
1

要获取每一列:

SELECT * FROM questions
WHERE id IN 
(SELECT max(id) as id, asker
FROM questions 
GROUP by asker 
ORDER by id DESC)

@bluefeet 答案的改进版本。

于 2017-07-30T13:12:03.753 回答
1

这是因为AFTERORDER BY执行。 GROUP BY

试试这个:

SELECT * FROM questions
WHERE id IN 
(
    SELECT max(id) as id
    FROM questions 
    GROUP by asker 
    ORDER by id DESC
)
于 2019-04-29T12:53:38.480 回答