2012-01-23 11 views
8

Estoy tratando de mostrar una lista de registros de miembros, y tengo algunas tablas que estoy usando para mostrar lo que necesito.Subconsulta de MySQL - Encuentra solo el primer registro en una UNIÓN IZQUIERDA

Esa es la parte fácil. La parte con la que necesito ayuda es con una tabla que tiene muchos registros para cada registro miembro: Historial de inicio de sesión

Quiero mostrar solo la primera fila para cada registro miembro, que existe en la tabla Historial de inicio de sesión. De forma alternativa, es posible que desee flip flop y mostrar el último registro en la tabla de historial de inicio de sesión.

aquí es lo que tengo hasta ahora:

SELECT m.memberid, m.membername, m.gender, mp.phone 
FROM tbl_members m, 
    tbl_members_phones mp, 
    tbl_members_addresses ma 
WHERE m.defaultphoneid = mp.phoneid 
AND m.defaultaddressid = ma.addressid 

Así que devuelve lo que se espera.

Las 2 columnas de tbl_members_login_history que quisiera agregar al resultado son: mh. loggedtime, mh. ipaddy

Sé que la adición de la tbl_members_login_history como un LEFT JOIN volvería duplicados, así que estoy pensando que debe haber una necesidad Subconsulta aquí, con el fin de devolver sólo el primero récord para ese memberid que existe en tbl_members_login_history.

Lo que me preocupa es que si no existe ningún registro en la tabla de historial, todavía quiero mostrar esa información de miembro, pero dejo las columnas de historial como NULL.

¿Esto sería un incidente de subconsulta? y si es así, ¿cómo se agrega ese tipo de LIMIT?

+0

Por curiosidad, ¿se requiere un teléfono y una dirección para un miembro? De lo contrario, no creo que su consulta devuelva la información del otro miembro si falta una de ellas en la forma en que está escrita. – Sam

+0

En este caso, sí. Pero tiene razón, el miembro no sería devuelto en el resultado sin un registro existente. – coffeemonitor

Respuesta

20

Este es el problema greatest-n-per-group, que se solicita con frecuencia en Desbordamiento de pila.

Así es como lo resolvería en su escenario:

SELECT m.memberid, m.membername, m.gender, mp.phone, mh.loggedtime, mh.ipaddy 
FROM tbl_members m 
INNER JOIN tbl_members_phones mp ON m.defaultphoneid = mp.phoneid 
INNER JOIN tbl_members_addresses ma ON m.defaultaddressid = ma.addressid 
LEFT OUTER JOIN tbl_members_login_history mh ON m.memberid = mh.memberid 
LEFT OUTER JOIN tbl_members_login_history mh2 ON m.memberid = mh2.memberid 
    AND mh.pk < mh2.pk 
WHERE mh2.pk IS NULL; 

Es decir, queremos mh ser la más reciente disputa en tbl_member_login_history para la memberId dado. Entonces buscamos otra fila mh2 que es aún más reciente. Si no se encuentra ninguna fila más reciente que mh, entonces mh2.* será NULL, por lo que mh debe ser el más reciente.

Supongo que esta tabla tiene una columna de clave principal que contiene valores crecientes. Para este ejemplo, supongo que el nombre de la columna es pk.

Usando combinación externa izquierda para ambas referencias a la tabla de historial de acceso significa que la fila m se informó incluso si no hay una fila coincidente.

+0

Acabo de probar su solución y la comparé con una consulta que contiene una subconsulta que utiliza 'GROUP BY' con' MAX (...) ', uniendo' ON date = maxdate'.Su solución con el operador '<' tomó 2s (!), Mientras que la solución de subconsulta tomó 0.01s (la misma tabla de resultados, por supuesto). El 'EXPLAIN' de su consulta se ve mucho mejor que el de la subconsulta. No sé por qué obtengo esta gran diferencia de rendimiento, pero supongo que es porque el '<' produce un gran resultado intermedio. – steffen

+0

@steffen: Sí, desde que contesté esta pregunta, he visto muchos otros casos, y concluí que la solución anterior o una solución como la que describes * puede * ser más rápida, dependiendo de cuántos grupos distintos y cuántos filas por grupo que tienen los datos. Entonces, es mejor probar ambos métodos con sus datos y luego decidir. –

+0

cierto. Lo que me confundió fue la gran diferencia de rendimiento. Encontré tu solución un par de veces aquí en SO. Y la salida 'EXPLAIN' se ve muy bien, así que esperaba que la consulta fuera muy rápida. Pero en cambio fue mucho más lento. Extraño. – steffen

0

añadir como esto

LEFT OUTER JOIN (SELECT member_id, MAX(LAST_LOGIN_DATE) from tbl_members_login_history) Last_Login ON Last_Login.memberid = m.memberid 

PS. LAST_LOGIN_DATE es pseudocolumna, puede probar su columna de restictive

Cuestiones relacionadas