110

電子メールのテーブルから一連の行を選択し、送信者ごとにグループ化できるようにしたいと思います。私のクエリは次のようになります。

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にそのレコードを選択させるにはどうすればよいですか?

4

6 に答える 6

149

簡単な解決策は、最初にORDERステートメントを使用してクエリを副選択にラップし、後でGROUPBYを適用することです。

SELECT * FROM ( 
    SELECT `timestamp`, `fromEmail`, `subject`
    FROM `incomingEmails` 
    ORDER BY `timestamp` DESC
) AS tmp_table GROUP BY LOWER(`fromEmail`)

これは結合の使用に似ていますが、見た目がはるかに良くなります。

GROUPBY句を指定したSELECTで非集計列を使用することは非標準です。MySQLは通常、最初に見つけた行の値を返し、残りを破棄します。ORDER BY句は、返された列の値にのみ適用され、破棄された列の値には適用されません。

重要な更新 実際に機能するために使用される非集計列の選択ですが、信頼するべきではありません。MySQLのドキュメントによると、「これは主に、GROUP BYで指定されていない各非集計列のすべての値が各グループで同じである場合に役立ちます。サーバーは各グループから任意の値を自由に選択できるため、同じでない限り、値は選ばれるのは不確定です。」

5.7.5以降、ONLY_FULL_GROUP_BYはデフォルトで有効になっているため、非集計列はクエリエラーを引き起こします(ER_WRONG_FIELD_WITH_GROUP)

@mikepが以下で指摘しているように、解決策は5.7以降のANY_VALUE()を使用することです。

http://www.cafewebmaster.com/mysql-order-sort-group https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html https://dev.mysql を参照して ください。 .com / doc / refman / 5.7 / en / group-by-handling.html https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_any-value

于 2012-03-21T00:45:45.777 に答える
47

すでに返信で指摘されているように、GROUP BYはウィンドウからレコードを任意に選択するため、現在の回答は間違っています。

MySQL 5.6、またはMySQL 5.7を使用してONLY_FULL_GROUP_BYいる場合、正しい(決定論的)クエリは次のとおりです。

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()ほとんどの場合使用されないを削除したことに注意してください。

于 2016-02-17T12:00:28.350 に答える
44

これが1つのアプローチです:

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
于 2009-06-30T22:59:22.747 に答える
29

次のようにクエリをGROUPBYでラップして、ORDERBYの後にGROUPBYを実行します。

SELECT t.* FROM (SELECT * FROM table ORDER BY time DESC) t GROUP BY t.from
于 2013-04-30T19:53:32.223 に答える
22

SQL標準によると、選択リストで非集計列を使用することはできません。MySQLはそのような使用を許可します(ONLY_FULL_GROUP_BYモードが使用されない限り)が、結果は予測できません。

ONLY_FULL_GROUP_BY

最初にfromEmail、MIN(read)を選択し、次に2番目のクエリ(またはサブクエリ)-Subjectを選択する必要があります。

于 2009-06-30T22:59:27.427 に答える
4

サブクエリのアプローチは、どのインデックスを付けてもひどく非効率的であり、Hibernateを介して外部の自己結合を取得できなかったため、これらのアプローチの両方で、示されているものよりも複雑なクエリに苦労しました。

これを行うための最良の(そして最も簡単な)方法は、必要なフィールドの連結を含むように構築されたものでグループ化し、SELECT句の式を使用してそれらを引き出すことです。MAX()を実行する必要がある場合は、MAX()を実行するフィールドが常に連結エンティティの最上位にあることを確認してください。

これを理解するための鍵は、これらの他のフィールドがMax()を満たすエンティティに対して不変である場合にのみクエリが意味をなすため、ソートに関しては、連結の他の部分を無視できることです。このリンクの一番下にこれを行う方法が説明されています。http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html

挿入/更新イベント(トリガーなど)を取得してフィールドの連結を事前に計算できる場合は、インデックスを作成できます。クエリは、group byが実際にMAX( )。複数のフィールドの最大数を取得するために使用することもできます。ネストされたセットとして表現された多次元ツリーに対してクエリを実行するために使用します。

于 2012-10-31T14:00:56.690 に答える