4

私は2つのテーブルを持っています:

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 が最も小さい行のように見えます。

どんなアドバイスでも大歓迎です!

4

2 に答える 2

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標準自体)では、あいまいさのために、このようなクエリを作成することは許可されていません。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行()が他にない行()を探します。それ以上が見つからない場合、LEFTOUTERJOINは一致ではなくNULLを返します。a2ticket_idincoming_timeincoming_time

于 2009-06-11T02:51:01.007 に答える
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);
于 2009-06-11T02:33:24.300 に答える