【问题标题】:group by behavior [duplicate]按行为分组[重复]
【发布时间】:2017-05-10 15:38:00
【问题描述】:

我需要在他们的组中找到价值最大的行。

我做了很多研究,找到了很多解决方案:join、临时表等。

我仍然想用“order by ... having ...”来尝试一些东西,但我发现了一些奇怪的东西。

这是我的查询:

SELECT MAX(id) AS MaxValue, MyTable.* 
FROM MyTable
GROUP BY name
HAVING id = MaxValue

我得到一个行列表作为结果,但我没有每个名字都有一行。

当我这样做时,我获得了很多行:

SELECT * FROM MyTable WHERE name = 'John'

但是第一个查询的结果不包含任何名为“John”的行,如果我这样做,我什么也得不到:

SELECT MAX(id) AS MaxValue, MyTable.* 
FROM MyTable
GROUP BY name
HAVING id = MaxValue AND name = 'John'

我真的不明白它是如何工作的,它触发了我。

谁能解释我这种行为?

我没有问“怎么做”。我只是不明白“通过...拥有分组”是如何工作的。

【问题讨论】:

  • “我需要在它们的组中找到具有最大价值的行。” -- GROUP BY 对此无济于事。它不会从表中返回行,而是使用每个组中的数据生成新行。它计算来自SELECT 子句的每个表达式,独立于其他表达式。作为一般规则,SELECT * ... GROUP BY 是无效的 SQL。
  • 你对SELECT * ... GROUP BY 说“无效”是什么意思?我使用 mySQL 并没有收到任何错误。你的意思是这是不好的做法?
  • Invalid 表示不正确。 SQL 标准对可以出现在GROUP BY 查询的SELECT 子句中的表达式设置了一些限制(这是有充分理由的)。大多数 RDBMS 遵循标准并拒绝无效查询。在 5.7.5 版本之前,MySQL 接受它们,但 takes its freedom to return indeterminate results。从 5.7.5 开始,行为是可配置的(默认情况下它会拒绝它们),我猜将来它不会再接受它们了。阅读更多:dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
  • 我明白了。我想当我这样做时也会出现同样的问题:SELECT id, MAX(id) AS MaxValue, FROM MyTable GROUP BY name HAVING id = MaxValue
  • 看看this answer。如果您查询单个表,那么您会从该答案中获得想法并编写符合您需求的查询。这是完成这项工作的最快方法。如果您需要查询多个表(使用JOINs),那么您最好尝试以下答案中提供的查询之一。他们不会跑那么快,但他们会在没有其他问题的情况下完成这项工作。

标签: mysql group-by greatest-n-per-group


【解决方案1】:

如果您希望按名称显示最大 id 组的行,则方法是

  select * from 
  MyTable where id in (SELECT MAX(id) 
        FROM MyTable
        GROUP BY name
   )

或者如果你只需要 john 的行

  select * from 
  MyTable where id in (SELECT MAX(id) 
        FROM MyTable
        GROUP BY name
   )
  where name = 'John';

【讨论】:

  • 感谢您的建议,但我已经知道了。我的问题是我不理解第一个查询的行为。为什么我缺少行?
  • 那种选择格式让我抓狂:/
  • 这假设idMyTable 中的主键或唯一值,即使这是一个好的假设并不能保证。
  • @JuanCarlosOropeza 可能是 .. 希望 OP 给我们一些建议 .. ..
【解决方案2】:

您需要做的是计算每个名称的最大 id。为此,您可以使用简单的GROUP BY

SELECT name, MAX(id) as MaxValue
FROM YourTable
GROUP BY name

现在您执行JOININ 比较以查看哪一行具有MaxValue。这有很多方法可以做到。您应该始终检查 EXPLAIN PLAN 以了解优化器在做什么以及是否使用了正确的索引。

我宁愿使用JOIN 方法。您尝试将表与上一个查询中已找到的值相匹配的位置。

SELECT *
FROM YourTable YT
JOIN ( SELECT name, MAX(id) as MaxValue
       FROM YourTable
       GROUP BY name 
     ) MaxT
  ON YT.name = MaxT.name
 AND YT.id = MaxT.MaxValue

现在,如果 ID 不是每个名称的唯一值,这可能会出现问题,并且可能返回重复的行,因此如果只需要一个结果,则需要额外考虑。

还可以选择对每一行使用子查询。在这里,您从外部表中获取name,并使用它在内部子查询中查找该名称的Max(ID)。这种方法也可能有重复的问题

 SELECT *
 FROM YourTable YT
 WHERE id = ( SELECT MAX(MaxT.id)
              FROM YourTable MaxT
              WHERE MaxT.name = YT.name )

【讨论】:

  • 我已经发现了这个解决方案。还是有它在这里很好!
  • 我注意到写SELECT name, MAX(id) as MaxValue FROM YourTable GROUP BY name 是可以的。但这不是:SELECT any_other_column, name, MAX(id) as MaxValue FROM YourTable GROUP BY name。是否有规则说,当使用“分组依据”时,选定的值只能是用于组或特定功能的字段?
  • 是的,有一个规则....但是在 mysql 中你可以忽略它(顺便说一句,我们不喜欢那个)。新版本有办法打开/关闭该规则。但规则是 select 上的每个字段都必须在 GROUP BY 上或在 MAXCOUNTSUM 等聚合函数中
  • 你还有问题吗?
  • 现在更清楚 GROUP BY 的工作原理了。我被这句话触发了,现在我更好地理解了。谢谢!
猜你喜欢
  • 2014-02-26
  • 2020-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-14
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
相关资源
最近更新 更多