【问题标题】:MySQL group and select only first from each group [duplicate]MySQL组并从每个组中仅选择第一个[重复]
【发布时间】:2018-12-29 02:17:41
【问题描述】:

我想执行一个查询,只选择给定组中的最新项目。


在这个例子中,我正在跟踪货车:

  • 每次他们返回基地时,都会记录一个check-in 以及里程等信息...
  • 每次交货时,都会记录 delivery - 客户等...

此表让我们知道给定 van 的历史记录。数据可以通过查询生成或随时存储 - 这不是问题。

 id | checkin_id | delivery_id | van_id
----+------------+-------------+--------
 24 | 15         | NULL        | 3
 25 | NULL       | 28          | 3
 26 | 16         | NULL        | 4
 27 | NULL       | 29          | 3
 28 | NULL       | 30          | 4
 29 | 17         | NULL        | 5

我可以通过... WHERE van_id=3; 查询来查看货车的历史记录 - 很好。

相反,我希望能够获得vans 的列表以及他们最近的“事件”。结果是这样的:

 id | checkin_id | delivery_id | van_id
----+------------+-------------+--------
 27 | NULL       | 29          | 3
 28 | NULL       | 30          | 4
 29 | 17         | NULL        | 5

我跳转到以下查询:

SELECT * FROM `history` GROUP BY `van_id`;

但这会返回以下错误:

#1055 - SELECT 列表的表达式 #1 不在 GROUP BY 子句中,并且包含非聚合列“database.history.checkin_id”,它在功能上不依赖于 GROUP BY 子句中的列;这与 sql_mode=only_full_group_by 不兼容

阅读后,我明白这是什么意思,并且不得不承认我的 SQL 有点过时了 - 我希望返回组中的哪些项目?

checkin_iddelivery_id 添加到GROUP BY 只会改变问题 - 最终我会得到相同的数据集,只是排序不同。


This answer 引起了我的兴趣,该图确实有助于清晰地勾勒出问题,感谢@azerafati!

我想使用 FIRST()LAST() 聚合函数 - but MySQL doesn't appear to have them

如何在不处理应用程序中的所有数据的情况下重现此行为?

【问题讨论】:

  • 学会正确使用GROUP BY。那么你就不会出现这个错误了。
  • 你得到这个特定错误的原因是因为 MySQL 的最新版本默认不允许它。据我所知,MySQL 是唯一允许它的 RDBMS。问题是,除非选择的非分组、非聚合字段在“功能上依赖于”分组字段,否则所使用的值不能保证是遇到的特定值(实际上是从分组过程中遇到的那些随机选择)。基本上,如果您的配置允许,您的查询可能会得到类似 (27, 15, 28, 3) 的结果行。
  • 你可以看出你的代码没有意义。当您按van_id 分组时,您没有对checkin_iddelivery_id 进行聚合,MySQL 怎么知道要获取哪一个?对于van_id 3,为什么checkin_idNULL,而不是15?为什么delivery_id29,而不是NULL???
  • 您正在尝试使用 MySQL 已弃用的非标准扩展 GROUP BY。请阅读this

标签: mysql greatest-n-per-group


【解决方案1】:

我猜你的id 值是唯一的,并且后面的记录比以前的记录具有更高的值。

您需要使用子查询为每辆货车获取最新的id

         SELECT MAX(id) id, van_id
           FROM history
          GROUP BY van_id

然后将其加入您的详细查询。

 SELECT h.*
   FROM history h
   JOIN (
         SELECT MAX(id) id, van_id
           FROM history
          GROUP BY van_id
        ) m ON h.id = m.id AND h.van_id = m.van_id

但由于您的 id 值是唯一的,您可以进一步简化此操作。

 SELECT h.*
   FROM history h
   JOIN (
         SELECT MAX(id) id
           FROM history
          GROUP BY van_id
        ) m ON h.id = m.id

【讨论】:

    【解决方案2】:

    我打算将此标记为重复,因为该问题实际上被问得相当频繁,但我发现这些问题/答案似乎很难搜索;所以这里是通用模板:

    SELECT t.*
    FROM theTable AS t
    INNER JOIN (
       SELECT groupingValue, MIN(someValue) AS lowestValue
       FROM theTable
       GROUP BY groupingValue
    ) AS rIdent ON rIdent.groupingValue = t.groupingValue AND rIdent.lowestValue= t.someValue
    

    最低 在您的特定情况下是 min(id)...哦,哎呀;你的问题先说,但细节说的是最近的(我会解释为最后一个),所以只需使用 MAX 而不是 MIN。 ...“groupingValue”是 van_id。

    编辑:如果分组字段上有索引并且用于识别最低/第一/最高/最近的字段,则查询应该相当有效。

    【讨论】:

    • 谢谢 - 我会玩...我已经解决了第一个/最近的歧义。
    • 类似的问题都应该标记greatest-n-per-group
    猜你喜欢
    • 2018-08-17
    • 2013-05-07
    • 2011-02-13
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    相关资源
    最近更新 更多