MySQL INNER JOIN выбирает только одну строку из второй таблицы


у меня есть 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
8 66

8 ответов:

вы должны иметь подзапрос, чтобы получить их последний день в 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
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

   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

есть две проблемы с ваш запрос:

  1. каждая таблица и подзапрос нуждается в имени, поэтому вы должны назвать подзапрос INNER JOIN (SELECT ...) AS p ON ....
  2. подзапрос как у вас, он возвращает только один срок подряд, но вы на самом деле хотите одну строку для каждого пользователя. Для этого вам нужен один запрос, чтобы получить максимальную дату, а затем самостоятельно вернулся обратно, чтобы получить всю строку.

предполагая, что нет никаких связей для 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

Матей Михай дал простое и эффективное решение, но оно не будет работать, пока не поставит MAX(date) в SELECT part так что этот запрос станет:

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. Я попробовал, и это сработало для меня.

ответ@John Woo помог мне решить аналогичную проблему. Я улучшил его ответ, установив правильный порядок. Это сработало для меня:

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

Я не уверен, насколько это эффективно, хотя.

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
)

Это решение лучше, чем принято отвечать потому что он работает правильно, когда есть некоторые платежи с тем же пользователем и датой.

мой ответ, непосредственно вдохновленный @valex, очень полезен, если вам нужно несколько cols в предложении ORDER BY.

    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