first - like inner join mysql
MySQL INNER JOIN selecciona solo una fila de la segunda tabla (8)
Tengo una tabla de users
y una tabla de payments
, para cada usuario, aquellos que tienen pagos, pueden tener varios pagos asociados en la tabla de payments
. Me gustaría seleccionar a todos los usuarios que tienen pagos, pero solo seleccionar sus últimos pagos. Estoy probando este SQL pero nunca he intentado declaraciones SQL anidadas antes, así que quiero saber qué estoy haciendo mal. Apreciar la ayuda
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
Debe tener una subconsulta para obtener su última fecha por 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
Hay dos problemas con su consulta:
- Cada tabla y subconsulta necesita un nombre, por lo que debe nombrar la subconsulta
INNER JOIN (SELECT ...) AS p ON ...
- La subconsulta tal como la tiene solo devuelve un período de fila, pero realmente desea una fila para cada usuario . Para eso, necesita una consulta para obtener la fecha máxima y luego volver a unirse automáticamente para obtener toda la fila.
Suponiendo que no existen vínculos para payments.date
, intente:
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
La respuesta de John Woo me ayudó a resolver un problema similar. He mejorado su respuesta estableciendo el orden correcto también. Esto me ha funcionado:
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
Aunque no estoy seguro de cuán eficiente es esto.
Matei Mihai dio una solución simple y eficiente, pero no funcionará hasta que ponga un MAX(date)
en la parte SELECT para que esta consulta se convierta en:
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
Y ordenar por no hará ninguna diferencia en la agrupación, pero puede ordenar el resultado final proporcionado por el grupo. Lo intenté y funcionó para mí.
Mi respuesta directamente inspirada de @valex es muy útil, si necesita varios cols en la cláusula 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
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
SELECT u.*, p.*
FROM users AS u
INNER JOIN payments AS p ON p.user_id = u.id AND u.id = (
SELECT id
FROM payments AS p2
WHERE p2.user_id = p.user_id
ORDER BY date DESC
LIMIT 1
)
Esta solución es mejor que la respuesta aceptada porque funciona correctamente cuando hay algunos pagos con el mismo usuario y fecha.
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
Echa un vistazo a este sqlfiddle