2009-06-11 3 views
4

У меня есть две таблиц:MYSQL показывает неправильные строки при использовании GROUP BY

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

где билеты представляют поток писем между вспомогательным персоналом и клиентами, а также статьями отдельными сообщений, которые составляют поток.

Я ищу, чтобы найти статью с наивысшим входящему времени (выраженное в виде метки времени 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 

Например,

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

:ticket: 
id --- queue_id 
1  1 
... 

Но результат выглядит как строка с наименьшим идентификатором статьи вместо того, что я ищу, что является статьей с самым высоким временем поступления.

Любой совет будет рад!

ответ

16

Это классический барьер, с которым сталкиваются большинство программистов 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 стандарт - вы не допускаются написать запрос, как это, из-за неоднозначности. Вы не можете включить ни один столбец в список выбора, который не находится внутри агрегированной функции или не указан в предложении 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; 

Другими словами, обратите внимание на строку (a1), для которых не существует никакой другой строки (a2) с той же ticket_id и большее incoming_time. Если не найдено более incoming_time, LEFT OUTER JOIN возвращает NULL вместо совпадения.

+0

Работал как шарм, и ответ был очень хорошо объяснен - ​​высоко ценится! – Han

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); 
Смежные вопросы