使用GROUP BY时,MYSQL显示不正确的行

Han*_*Han 4 mysql select group-by max greatest-n-per-group

我有两张桌子:

article('id', 'ticket_id', 'incoming_time', 'to', 'from', 'message')
ticket('id', 'queue_id')
Run Code Online (Sandbox Code Playgroud)

其中ticket表示支持人员和客户之间的电子邮件线程,而文章是组成线程的各个消息.

我希望找到具有最高的传入时间(表示为UNIX时间戳),每个TICKET_ID的文章,这也是我目前使用的查询:

SELECT article.* , MAX(article.incoming_time) as maxtime
FROM ticket, article
WHERE ticket.id = article.ticket_id
AND ticket.queue_id = 1
GROUP BY article.ticket_id
Run Code Online (Sandbox Code Playgroud)

例如,

:article:
id --- ticket_id --- incoming_time --- to ------- from ------- message --------
11     1             1234567           help@      client@      I need help...   
12     1             1235433           client@    help@        How can we help?
13     1             1240321           help@      client@      Want food!    
...

:ticket:
id --- queue_id
1      1
...
Run Code Online (Sandbox Code Playgroud)

但结果看起来是具有最小文章ID的行而不是我正在寻找的那个具有最高传入时间的文章.

任何建议将不胜感激!

Bil*_*win 17

这是大多数MySQL程序员遇到的经典障碍.

  • 你有一个列ticket_id的参数GROUP BY.此列中的不同值定义组.
  • 你有一个列incoming_time的参数MAX().此列中对每个组中的行的最大值将作为值返回MAX().
  • 您拥有表格文章的所有其他列. 为这些列返回的值是任意的,而不是来自MAX()发生值的同一行.

数据库无法推断您需要来自发生最大值的同一行的值.

考虑以下情况:

  • 有多行出现相同的最大值.应该使用哪一行来显示列article.*

  • 您编写一个返回MIN()和的查询MAX().这是合法的,但哪一行应该article.*显示?

    SELECT article.* , MIN(article.incoming_time), MAX(article.incoming_time)
    FROM ticket, article
    WHERE ticket.id = article.ticket_id
    AND ticket.queue_id = 1
    GROUP BY article.ticket_id
    
    Run Code Online (Sandbox Code Playgroud)
  • 您使用聚合函数,例如AVG()or SUM(),其中没有行具有该值.数据库如何猜测要显示哪一行?

    SELECT article.* , AVG(article.incoming_time)
    FROM ticket, article
    WHERE ticket.id = article.ticket_id
    AND ticket.queue_id = 1
    GROUP BY article.ticket_id
    
    Run Code Online (Sandbox Code Playgroud)

在大多数品牌的数据库中 - 以及SQL标准本身 - 由于含糊不清,不允许您编写这样的查询.您不能在select-list中包含任何不在聚合函数内或在GROUP BY子句中命名的列.

MySQL更宽松.它允许您执行此操作,并让您无需模糊地编写查询.如果确实存在歧义,则会从组中物理位置的行中选择值(但这取决于存储引擎).

对于它的价值,SQLite也有这种行为,但它选择组中的最后一行来解决歧义.去搞清楚.如果SQL标准没有说明要做什么,那就取决于供应商的实现.

这是一个可以为您解决问题的查询:

SELECT a1.* , a1.incoming_time AS maxtime
FROM ticket t JOIN article a1 ON (t.id = a1.ticket_id)
LEFT OUTER JOIN article a2 ON (t.id = a2.ticket_id 
  AND a1.incoming_time < a2.incoming_time)
WHERE t.queue_id = 1
  AND a2.ticket_id IS NULL;
Run Code Online (Sandbox Code Playgroud)

换句话说,查找一个row(a1),其中没有其他row(a2)具有相同ticket_id和更大的行incoming_time.如果未incoming_time找到更大,则LEFT OUTER JOIN返回NULL而不是匹配.