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

时间:2009-06-11 02:28:38

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

我有两张桌子:

article('id', 'ticket_id', 'incoming_time', 'to', 'from', 'message')
ticket('id', 'queue_id')

其中,故障单代表支持人员和客户之间的电子邮件主题,而文章是构成主题的各个消息。

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

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

例如,

: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
...

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

非常感谢任何建议!

2 个答案:

答案 0 :(得分: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
    
  • 您使用AVG()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
    

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

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

对于它的价值,SQLite也有这种行为,但它选择组中的 last 行来解决歧义。去搞清楚。如果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;

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

答案 1 :(得分:3)

SELECT a1.* FROM article a1 
JOIN 
  (SELECT MAX(a2.incoming_time) AS maxtime
   FROM article a2
   JOIN ticket ON (a2.ticketid=ticket.id)
   WHERE ticket.queue_id=1) xx
  ON (a1.incoming_time=xx.maxtime);