我希望能够从电子邮件表中选择一堆行,并由发件人分组。我的查询如下:
SELECT
`timestamp`, `fromEmail`, `subject`
FROM `incomingEmails`
GROUP BY LOWER(`fromEmail`)
ORDER BY `timestamp` DESC
查询几乎按我的意愿运行 - 它选择通过电子邮件分组的记录。问题是主题和时间戳与特定电子邮件地址的最新记录不对应。
例如,它可能会返回:
fromEmail: john@example.com, subject: hello
fromEmail: mark@example.com, subject: welcome
当数据库中的记录为:
时fromEmail: john@example.com, subject: hello
fromEmail: john@example.com, subject: programming question
fromEmail: mark@example.com, subject: welcome
如果“编程问题”主题是最新的,如何在分组电子邮件时让MySQL选择该记录?
答案 0 :(得分:131)
一个简单的解决方案是使用ORDER语句 first 将查询包装到子选择中,然后再应用GROUP BY :
SELECT * FROM (
SELECT `timestamp`, `fromEmail`, `subject`
FROM `incomingEmails`
ORDER BY `timestamp` DESC
) AS tmp_table GROUP BY LOWER(`fromEmail`)
这类似于使用连接但看起来更好。
在带有GROUP BY子句的SELECT中使用非聚合列是非标准的。 MySQL通常会返回它找到的第一行的值,并丢弃其余的行。任何ORDER BY子句仅适用于返回的列值,而不适用于丢弃的列值。
重要更新 选择用于实际工作但不应依赖的非聚合列。根据{{3}}“这主要适用于每个未在GROUP BY中命名的非聚合列中的所有值对于每个组都相同。服务器可以从每个组中自由选择任何值 ,所以除非它们相同,否则所选择的值是不确定的。“
从5.6.21开始,我注意到临时表上GROUP BY的问题还原了ORDER BY排序。
从MySQL documentation开始默认启用ONLY_FULL_GROUP_BY,即无法使用非聚合列。
请参阅 5.7.5 http://www.cafewebmaster.com/mysql-order-sort-group https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html
答案 1 :(得分:41)
这是一种方法:
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)
基本上,您将表连接到自身,搜索以后的行。在where子句中,您声明不能有以后的行。这只给你最新一行。
如果可能有多个具有相同时间戳的电子邮件,则此查询将需要优化。如果电子邮件表中有增量ID列,请将JOIN更改为:
LEFT JOIN incomingEmails next
on cur.fromEmail = next.fromEmail
and cur.id < next.id
答案 2 :(得分:29)
在ORDER BY之后执行GROUP BY,方法是使用GROUP BY包装查询:
SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from
答案 3 :(得分:23)
正如已经回答的那样,当前答案是错误的,因为GROUP BY从窗口中任意选择记录。
如果正在使用MySQL 5.6或带有ONLY_FULL_GROUP_BY
的MySQL 5.7,则正确(确定性)查询是:
SELECT incomingEmails.*
FROM (
SELECT fromEmail, MAX(timestamp) `timestamp`
FROM incomingEmails
GROUP BY fromEmail
) filtered_incomingEmails
JOIN incomingEmails USING (fromEmail, timestamp)
GROUP BY fromEmail, timestamp
为了使查询有效运行,需要正确的索引。
请注意,出于简化目的,我删除了LOWER()
,在大多数情况下,我不会使用{。}}。
答案 4 :(得分:21)
根据SQL标准,您不能在选择列表中使用非聚合列。 MySQL允许这样的使用(使用uless ONLY_FULL_GROUP_BY模式),但结果是不可预测的。
您应首先选择fromEmail,MIN(读取),然后选择第二个查询(或子查询) - Subject。
答案 5 :(得分:2)
对于比这些更复杂的查询,我对这两种方法都很挣扎,因为无论我使用什么索引,子查询方法都非常缺乏,并且因为我无法通过Hibernate获得外部自连接
执行此操作的最佳(也是最简单)方法是按构造的内容进行分组,以包含所需字段的串联,然后使用SELECT子句中的表达式将它们拉出来。如果需要执行MAX(),请确保您想要MAX()的字段始终位于连接实体的最重要的末尾。
理解这一点的关键是,只有当这些其他字段对于满足Max()的任何实体都是不变的时,查询才有意义,因此就排序而言,可以忽略其他部分的连接。它解释了如何在此链接的最底部执行此操作。 http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html
如果您可以获得插入/更新事件(如触发器)来预先计算字段的串联,您可以将其编入索引,并且查询将与组合的结果一样快,只是您实际想要的字段到MAX()。您甚至可以使用它来获取最多的多个字段。我使用它来对表达为嵌套集的多维树进行查询。