0

最新の ID と日付に基づいてデータのサブセットを取得しようとしています。テーブル内の他のフィールドを選択すると、返される最大 ID および日付と同期していないようです。

どうすればこれを修正できますか?

MySQL:

SELECT MAX(m.id) as id, m.sender_id, m.receiver_id, MAX(m.date) as date, m.content, l.username, p.gender 
FROM  messages m 
LEFT JOIN login_users l on l.user_id = m.sender_id 
LEFT JOIN profiles p ON p.user_id = l.user_id 
WHERE m.receiver_id=3
GROUP BY m.sender_id ORDER BY date DESC LIMIT 0, 7

content のデータが正しくありません。最大IDと最大日付の行に関連付けられているコンテンツではなく、ランダムなコンテンツを返しているようです。

これを修正するには、何らかのサブセレクトを行う必要がありますか?

4

3 に答える 3

3

タイトルの「コンテンツ フィールドが MAX(id) フィールドと一致しないのはなぜですか」という質問に答えるには、非集計フィールドに対して返される値が MAX 値の行から取得されるという保証がないためです。見つかった。これは文書化された動作であり、これが期待される動作です。

他の DBMS はステートメントでエラーをスローします。MySQL はより緩く、1 つの行から値を取得していますが、MAX 値 (ID または日付) のいずれかが見つかった行であるとは限りません。

2 つの別個の集計式MAX(m.id)とがありMAX(m.date)ます。これらの値が同じ行から取得されるという保証はないことに注意してください。

他のデータベースのルールは、SELECT リスト内のすべての非集計式が GROUP BY に表示される必要があるというものです。(MySQL はそれについてより緩く、それを要件にしません。)

MAX 値を持つ行から値を返すようにクエリを「修正」する 1 つの方法は、インライン ビュー (クエリ) を使用して、MAX(id)GROUP BY したいものでグループ化してから、JOIN を元に戻すことです。 table を使用して、行の他の値を取得します。

あなたの声明から、どの結果セットを返したいのか明確ではありません。最大の id を持つ行が必要で、最大の日付を持つ行も必要な場合は、次のようにすることができます。

SELECT m.id
     , m.sender_id
     , m.receiver_id
     , m.date
     , m.content
     , l.username
     , p.gender 
  FROM ( SELECT t.sender_id
              , t.receiver_id
              , MAX(t.id) AS max_id
              , MAX(t.date) AS max_date
           FROM messages t
          WHERE t.receiver_id=3
          GROUP
             BY t.sender_id
              , t.receiver_id
       ) s
  JOIN messages m 
    ON m.sender_id = s.sender_id
   AND m.receiver_id = s.receiver_id
   AND ( m.id = s.max_id OR m.date = s.max_date)
  LEFT
  JOIN login_users l on l.user_id = m.sender_id 
  LEFT
  JOIN profiles p ON p.user_id = l.user_id
 ORDER BY m.date DESC LIMIT 0, 7

「s」としてエイリアス化されたインライン ビューは最大値を返し、「m」としてエイリアス化されたメッセージ テーブルに結合されます。

ノート

ほとんどの場合、アクセス プランが異なるため、 はJOIN (query)よりもパフォーマンスが優れていることがわかります。IN (query)EXPLAIN で計画の違いを確認できます。

パフォーマンスのために、インデックスが必要です

... ON messages (`receiver_id`, `sender_id`, `id`, `date`)

receiver_id には等価述語があるため、(フル スキャンの代わりに) 範囲スキャンを取得するには、それが先頭の列になる必要があります。次に列が必要ですsender_id。これにより、MySQL が「ファイルソートを使用する」操作を回避して行をグループ化できるようになるためです。および列が含まれiddateいるため、インライン ビュー クエリは、テーブル内のページにアクセスする必要なく、インデックス ページから完全に満たすことができます。(EXPLAIN には " Using where; Using index" が表示されます。)

その同じインデックスは外部クエリにも適しているはずですがcontent、テーブル ページから " " 列にアクセスする必要があるため、EXPLAIN はそのステップで "Using index" を表示しません。(" content" 列は、インデックスに必要な長さよりもはるかに長い可能性があります。)

于 2013-07-21T20:59:55.130 に答える
0

結合の使用

SELECT LatestM.id, m.sender_id, m.receiver_id, m.date, m.content, l.username, p.gender 
(
    SELECT sender_id, MAX(id) AS id
    FROM  messages 
    WHERE receiver_id=3
    GROUP BY sender_id 
) LatestM
INNER JOIN messages m 
ON LatestM.sender_id = m.sender_id AND LatestM.id = m.id
LEFT JOIN login_users l on l.user_id = m.sender_id 
LEFT JOIN profiles p ON p.user_id = l.user_id 
WHERE m.receiver_id = 3
ORDER BY date DESC 
LIMIT 0, 7

これに関する問題は、最新の ID が最新の日付を反映していない場合、返される日付が最新のものにならないことです。

于 2013-07-21T21:05:20.650 に答える