MySQL INNER JOINは2番目のテーブルから1行のみを選択します


104

私はusersテーブルとテーブルを持っています。payments各ユーザーは支払いを持っているため、テーブルに複数の関連付けられた支払いがある場合がありpaymentsます。支払いのあるすべてのユーザーを選択しますが、最新の支払いのみを選択します。私はこのSQLを試していますが、これまでにネストしたSQLステートメントを試したことがないので、何が間違っているのかを知りたいのです。ヘルプに感謝

SELECT u.* 
FROM users AS u
    INNER JOIN (
        SELECT p.*
        FROM payments AS p
        ORDER BY date DESC
        LIMIT 1
    )
    ON p.user_id = u.id
WHERE u.package = 1

回答:


148

ごとに最新の日付を取得するには、サブクエリが必要user IDです。

SELECT  a.*, c.*
FROM users a 
    INNER JOIN payments c
        ON a.id = c.user_ID
    INNER JOIN
    (
        SELECT user_ID, MAX(date) maxDate
        FROM payments
        GROUP BY user_ID
    ) b ON c.user_ID = b.user_ID AND
            c.date = b.maxDate
WHERE a.package = 1

1
@Fluffeh非常に良い答えがありPart 1 - Joins and Unionsます。:)ブックマークしました!
ジョンウー

1
メイトに感謝し、このような状況のためにそれを書いて、正しいSQLで簡単な答えをポップアップしてから、人々が提案されたコードを理解できるように、そのモンスターへのリンクを詳しく読んでください。追加してさらに拡張する計画です。よろしければ参加してください。コードのフィドルを表示します...
Fluffeh

@JohnWoo回答ありがとうございます。そして、Q&AについてFluffehに感謝します。
Wasim 2012

内部結合を1つだけ使用しているので、私の答えはより単純で高速だと思います。たぶん私は間違っています?
Mihai Matei 2012

2
内部結合なしでこのクエリを実行する方法はありますか?テーブルcから最大値を返すか、一致する行がない場合はnullを取得します。JOINをLEFT JOINに変更しても、明らかに機能しません。
スコット

32
SELECT u.*, p.*
FROM users AS u
INNER JOIN payments AS p ON p.id = (
    SELECT id
    FROM payments AS p2
    WHERE p2.user_id = u.id
    ORDER BY date DESC
    LIMIT 1
)

このソリューションは、同じユーザーと同じ日付の支払いがある場合に正しく機能するため、承認された回答よりも優れています。


それはあなたが使う良いアプローチです。しかし、私は好きな1つの製品に対して1つの画像を取得することについて質問があります。1つの製品に多くの(4)画像がありますが、その製品に対して1つの画像のみを表示したいと思います。
Ali Raza

使用が非常に遅くなると思いませんか?内部結合のサブクエリのすべてのレコードを処理しているため。受け入れられた回答は、微調整後の最良の回答になります。
ハミーズA.カーン

@ HameesA.Khan、クエリの実行を調べていません。あなたは正しいかもしれませんが、受け入れられた解決策は3次元の結合を作成しますが、これも遅い可能性があります。私は微調整がマイナーではないのではないかと心配しています(これが質問の主題です)。
Finesse

12
SELECT u.*, p.*, max(p.date)
FROM payments p
JOIN users u ON u.id=p.user_id AND u.package = 1
GROUP BY u.id
ORDER BY p.date DESC

このsqlfiddleをチェックしてください


6
このlimit 1句は、OPが必要としない1人のユーザーのみを返します。
Fluffeh 2012

6
@MateiMihai、これは機能しません。クエリは最大日付のみを提供し、最大日付を含む行全体を提供しません。あなたはそれをフィドルで見ることができます:date列はとは異なりmax(p.date)ます。paymentsテーブルに列を追加した場合(例cost
:)

3
   SELECT u.* 
        FROM users AS u
        INNER JOIN (
            SELECT p.*,
             @num := if(@id = user_id, @num + 1, 1) as row_number,
             @id := user_id as tmp
            FROM payments AS p,
                 (SELECT @num := 0) x,
                 (SELECT @id := 0) y
            ORDER BY p.user_id ASC, date DESC)
        ON (p.user_id = u.id) and (p.row_number=1)
        WHERE u.package = 1

2

クエリには2つの問題があります。

  1. すべてのテーブルとサブクエリには名前が必要なので、サブクエリに名前を付ける必要がありますINNER JOIN (SELECT ...) AS p ON ...
  2. あなたが持っているサブクエリは1行の期間のみを返しますが、実際にはユーザーごとに 1行が必要です。そのためには、最大の日付を取得するために1つのクエリが必要で、その後、行全体を取得するために自己結合します。

の関係がないと仮定してpayments.date、以下を試してください。

    SELECT u.*, p.* 
    FROM (
        SELECT MAX(p.date) AS date, p.user_id 
        FROM payments AS p
        GROUP BY p.user_id
    ) AS latestP
    INNER JOIN users AS u ON latestP.user_id = u.id
    INNER JOIN payments AS p ON p.user_id = u.id AND p.date = latestP.date
    WHERE u.package = 1

それはあなたが使う良いアプローチです。しかし、私は好きな1つの製品に対して1つの画像を取得することについて質問があります。1つの製品に多くの(4)画像がありますが、その製品に対して1つの画像のみを表示したいと思います。
Ali Raza

私の場合、これが最も高速であることがわかりました。私が行った唯一の変更はFrom payments as p Where p.user_id =@user_id、クエリがテーブル全体でグループ化を行っているときにのみ、選択したユーザーデータをフィルターするサブクエリにwhere句を追加することです。
Vikash Rathee

2

@ジョン・ウーの答えは私が同様の問題を解決するのに役立ちました。同様に正しい順序を設定することで、彼の答えを改善しました。これは私のために働きました:

SELECT  a.*, c.*
FROM users a 
    INNER JOIN payments c
        ON a.id = c.user_ID
    INNER JOIN (
        SELECT user_ID, MAX(date) as maxDate FROM
        (
            SELECT user_ID, date
            FROM payments
            ORDER BY date DESC
        ) d
        GROUP BY user_ID
    ) b ON c.user_ID = b.user_ID AND
           c.date = b.maxDate
WHERE a.package = 1

ただし、これがどれほど効率的かはわかりません。


2
SELECT U.*, V.* FROM users AS U 
INNER JOIN (SELECT *
FROM payments
WHERE id IN (
SELECT MAX(id)
FROM payments
GROUP BY user_id
)) AS V ON U.id = V.user_id

これでうまくいきます


1

Matei Mihaiはシンプルで効率的なソリューションを提供しましたがMAX(date)、SELECT部分​​を配置するまで機能しないため、このクエリは次のようになります。

SELECT u.*, p.*, max(date)
FROM payments p
JOIN users u ON u.id=p.user_id AND u.package = 1
GROUP BY u.id

また、order byによってグループ化に違いが生じることはありませんが、group byによって提供される最終結果を順序付けすることができます。試してみましたが、うまくいきました。


1

ORDER BY句にいくつかのcolが必要な場合、@ valexから非常に役立つ私の答えは直接役に立ちます。

    SELECT u.* 
    FROM users AS u
    INNER JOIN (
        SELECT p.*,
         @num := if(@id = user_id, @num + 1, 1) as row_number,
         @id := user_id as tmp
        FROM (SELECT * FROM payments ORDER BY p.user_id ASC, date DESC) AS p,
             (SELECT @num := 0) x,
             (SELECT @id := 0) y
        )
    ON (p.user_id = u.id) and (p.row_number=1)
    WHERE u.package = 1

1

あなたはこれを試すことができます:

SELECT u.*, p.*
FROM users AS u LEFT JOIN (
    SELECT *, ROW_NUMBER() OVER(PARTITION BY userid ORDER BY [Date] DESC) AS RowNo
    FROM payments  
) AS p ON u.userid = p.userid AND p.RowNo=1

1
このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
Alessio、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.