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 ответов:
вы должны иметь подзапрос, чтобы получить их последний день в
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
есть две проблемы с ваш запрос:
- каждая таблица и подзапрос нуждается в имени, поэтому вы должны назвать подзапрос
INNER JOIN (SELECT ...) AS p ON ...
.- подзапрос как у вас, он возвращает только один срок подряд, но вы на самом деле хотите одну строку для каждого пользователя. Для этого вам нужен один запрос, чтобы получить максимальную дату, а затем самостоятельно вернулся обратно, чтобы получить всю строку.
предполагая, что нет никаких связей для
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