wake-up-neo.net

MySQL INNER JOIN wählt nur eine Zeile aus der zweiten Tabelle aus

Ich habe eine users-Tabelle und eine payments-Tabelle für jeden Benutzer, dessen Zahlungen Zahlungen haben. In der payments-Tabelle können mehrere Zahlungen vorhanden sein. Ich möchte alle Benutzer auswählen, die Zahlungen haben, aber nur die letzte Zahlung auswählen. Ich versuche diese SQL, aber ich habe noch nie geschachtelte SQL-Anweisungen ausprobiert, also möchte ich wissen, was ich falsch mache. Schätzen Sie die Hilfe

        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
70
Wasim

Sie benötigen eine Unterabfrage, um das späteste Datum per user ID zu erhalten.

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
105
John Woo
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

Schauen Sie sich diese sqlfiddle

10
Matei Mihai
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
)

Diese Lösung ist besser als die akzeptierte Antwort , weil sie korrekt funktioniert, wenn Zahlungen mit demselben Benutzer und demselben Datum eingehen.

4
Finesse
   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
3
valex

Die Antwort von John Woo half mir, ein ähnliches Problem zu lösen. Ich habe seine Antwort verbessert, indem ich auch die richtige Reihenfolge einstelle. Das hat für mich funktioniert:

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

Ich bin nicht sicher, wie effizient das ist.

1
GTCrais

Matei Mihai hat eine einfache und effiziente Lösung gegeben, die jedoch erst dann funktioniert, wenn Sie einen MAX(date) in den SELECT-Teil einfügen. Diese Abfrage wird zu:

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

Und order by macht keinen Unterschied in der Gruppierung, aber es kann das von group zur Verfügung gestellte Endergebnis nach sortieren. Ich habe es versucht und es hat für mich funktioniert.

1
hassan

Es gibt zwei Probleme mit Ihrer Abfrage:

  1. Jede Tabelle und Unterabfrage benötigt einen Namen, daher müssen Sie die Unterabfrage INNER JOIN (SELECT ...) AS p ON ... benennen. 
  2. Die Unterabfrage gibt nur eine Zeilenperiode zurück, aber Sie möchten tatsächlich eine Zeile für jeden Benutzer . Dafür benötigen Sie eine Abfrage, um das maximale Datum zu erhalten, und dann selbst zurückkehren, um die gesamte Zeile zu erhalten.

Angenommen, es gibt keine Bindungen für payments.date, versuchen Sie Folgendes:

    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
lc.

Meine Antwort direkt von @valex inspiriert, sehr nützlich, wenn Sie mehrere Spalten in der ORDER BY-Klausel benötigen. 

    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
0
Jérôme B