MySQL"Group By"和"Order By"

Joh*_*lak 92 mysql sql group-by sql-order-by aggregate-functions

我希望能够从电子邮件表中选择一堆行,并通过发件人对它们进行分组.我的查询如下所示:

SELECT 
    `timestamp`, `fromEmail`, `subject`
FROM `incomingEmails` 
GROUP BY LOWER(`fromEmail`) 
ORDER BY `timestamp` DESC
Run Code Online (Sandbox Code Playgroud)

查询几乎按我的意愿运行 - 它选择通过电子邮件分组的记录.问题是主题和时间戳不对应于特定电子邮件地址的最新记录.

例如,它可能会返回:

fromEmail: john@example.com, subject: hello
fromEmail: mark@example.com, subject: welcome
Run Code Online (Sandbox Code Playgroud)

当数据库中的记录是:

fromEmail: john@example.com, subject: hello
fromEmail: john@example.com, subject: programming question
fromEmail: mark@example.com, subject: welcome
Run Code Online (Sandbox Code Playgroud)

如果"编程问题"主题是最新的,那么在分组电子邮件时如何让MySQL选择该记录呢?

b7k*_*ich 135

一个简单的解决方案是将查询包裹与ORDER语句子选择第一和应用GROUP BY :

SELECT * FROM ( 
    SELECT `timestamp`, `fromEmail`, `subject`
    FROM `incomingEmails` 
    ORDER BY `timestamp` DESC
) AS tmp_table GROUP BY LOWER(`fromEmail`)
Run Code Online (Sandbox Code Playgroud)

这类似于使用连接但看起来更好.

在带有GROUP BY子句的SELECT中使用非聚合列是非标准的.MySQL通常会返回它找到的第一行的值,并丢弃其余的行.任何ORDER BY子句仅适用于返回的列值,而不适用于丢弃的列值.

重要更新 选择用于实际工作但不应依赖的非聚合列.根据MySQL文档 "这主要是当每个非GROUPAG中未命名的非聚合列中的所有值对于每个组都相同时.服务器可以自由选择每个组中的任何值,因此除非它们相同,否则值被选中是不确定的."

从5.6.21开始,我注意到临时表上GROUP BY的问题还原了ORDER BY排序.

5.7.5开始,默认情况下启用ONLY_FULL_GROUP_BY,即无法使用非聚合列.

请参阅 http://www.cafewebmaster.com/mysql-order-sort-group https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html https://dev.mysql的.com/DOC/refman/5.7/EN /组逐handling.html

  • 几年前我提出了相同的解决方案,这是一个很好的解决方案.感谢b7kich.这里有两个问题但是...... GROUP BY是不区分大小写的,所以LOWER()是不必要的,其次,$ userID似乎是直接来自PHP的变量,如果$ userID是用户提供的而不是强制的,你的代码可能是sql注入漏洞是一个整数. (7认同)
  • 这是错误的,“ORDER BY”从子查询中被丢弃,从嵌套查询中选择的行是随机的。有时它可能会起作用,增加混乱,但这会导致噩梦般的错误。正确的答案在这里/sf/ask/74651741/#35456144 (6认同)

And*_*mar 42

这是一种方法:

SELECT cur.textID, cur.fromEmail, cur.subject, 
     cur.timestamp, cur.read
FROM incomingEmails cur
LEFT JOIN incomingEmails next
    on cur.fromEmail = next.fromEmail
    and cur.timestamp < next.timestamp
WHERE next.timestamp is null
and cur.toUserID = '$userID' 
ORDER BY LOWER(cur.fromEmail)
Run Code Online (Sandbox Code Playgroud)

基本上,您将表连接到自身,搜索以后的行.在where子句中,您声明不能有以后的行.这只给你最新的一行.

如果可能有多个具有相同时间戳的电子邮件,则此查询将需要优化.如果电子邮件表中有增量ID列,请更改JOIN,如:

LEFT JOIN incomingEmails next
    on cur.fromEmail = next.fromEmail
    and cur.id < next.id
Run Code Online (Sandbox Code Playgroud)


111*_*01b 29

通过使用GROUP BY包装查询,在ORDER BY之后执行GROUP BY:

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from
Run Code Online (Sandbox Code Playgroud)


Mar*_*cus 28

正如已经回答的那样,当前的答案是错误的,因为GROUP BY从窗口中任意选择记录.

如果使用MySQL 5.6或MySQL 5.7 ONLY_FULL_GROUP_BY,则正确(确定性)查询是:

SELECT incomingEmails.*
  FROM (
    SELECT fromEmail, MAX(timestamp) `timestamp`
    FROM incomingEmails
    GROUP BY fromEmail
  ) filtered_incomingEmails
  JOIN incomingEmails USING (fromEmail, timestamp)
GROUP BY fromEmail, timestamp
Run Code Online (Sandbox Code Playgroud)

为了使查询有效运行,需要正确的索引.

请注意,出于简化目的,我删除了LOWER(),在大多数情况下,不会使用它.

  • 这应该是正确的答案.我刚发现我的网站上有一个与此相关的错误.其他答案中的子选择中的`order by`根本没有效果. (2认同)

noo*_*nex 21

根据SQL标准,您不能在选择列表中使用非聚合列.MySQL允许这样的使用(使用uless ONLY_FULL_GROUP_BY模式)但结果是不可预测的.

ONLY_FULL_GROUP_BY

您应首先选择fromEmail,MIN(读取),然后选择第二个查询(或子查询) - Subject.