2012-01-23 11 views
8

Sto tentando di visualizzare un elenco di record membro e ho alcune tabelle che sto usando per visualizzare ciò di cui ho bisogno.Sottoquery MySQL - Trova solo il primo record in un SINISTRO SINISTRA

Questa è la parte facile. La parte che ho bisogno di aiuto è con una tabella che ha molti record per ogni record membro: Cronologia login

Voglio visualizzare solo la prima riga per ogni record membro, che esiste nella tabella Cronologia login. In alternativa, potrei voler capovolgere il flop e visualizzare l'ultimo record nella tabella Cronologia login.

ecco cosa ho finora:

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 

In modo che restituisce quello che ci si aspetta.

Le 2 colonne da tbl_members_login_history Vorrei aggiungere al risultato restituito: mh. loggedtime, mh. ipaddy

So che l'aggiunta del tbl_members_login_history come LEFT JOIN sarebbe tornato duplicati, così sto pensando ci deve essere una necessità sottoquery qui, al fine di restituire solo il primo record per quel memberid che esiste in tbl_members_login_history.

Ciò di cui sono preoccupato è se non esiste alcun record nella tabella della cronologia, voglio comunque visualizzare le informazioni sui membri, ma lasciare le colonne della cronologia come NULL.

Questo sarebbe un incidente di subquery? e se sì, come si aggiunge quel tipo di LIMIT?

+0

Per curiosità, sono necessari un telefono e l'indirizzo per un membro? In caso contrario, non penso che la tua query restituirà le altre informazioni sui membri se una di esse manca nel modo in cui è scritta. – Sam

+0

In questo caso, sì. Ma hai ragione, il membro non verrebbe restituito nel risultato senza un record esistente. – coffeemonitor

risposta

20

Questo è il problema greatest-n-per-group, che viene richiesto frequentemente su Stack Overflow.

Ecco come vorrei risolverlo nel vostro scenario:

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; 

Cioè, vogliamo mh essere la fila più recente nel tbl_member_login_history per il dato memberId. Quindi cerchiamo un'altra riga mh2 che è ancora più recente. Se non si trova nessuna più recente della riga mh, allora mh2.* sarà NULL, quindi mh deve essere il più recente.

Suppongo che questa tabella abbia una colonna chiave primaria che contiene valori crescenti. Per questo esempio, suppongo che il nome della colonna sia pk.

Utilizzare LEFT OUTER JOIN per entrambi i riferimenti alla tabella della cronologia di accesso indica che la riga m verrà segnalata anche se non è presente alcuna riga corrispondente.

+0

Ho appena provato la soluzione e l'ho confrontata con una query che contiene una sottoquery usando 'GROUP BY' con' MAX (...) ', unendo' ON date = maxdate'.La tua soluzione con l'operatore '<' ha preso 2s (!), Mentre la soluzione di subquery ha richiesto 0.01s (ovviamente la stessa tabella dei risultati). Il 'EXPLAIN' della tua query sembra molto meglio di quello con la sottoquery. Non so perché ottengo questa enorme differenza di prestazioni, ma suppongo che sia perché "<" produce un enorme risultato intermedio. – steffen

+0

@steffen: Sì, dal momento che ho risposto a questa domanda, ho visto molti altri casi e ho concluso che la soluzione sopra o una soluzione come te descrive * può * essere più veloce, a seconda di quanti gruppi distinti e di quanti righe per gruppo di dati. Quindi è meglio testare entrambi i metodi con i tuoi dati e poi decidere. –

+0

Vero. Ciò che mi ha confuso è stata l'enorme differenza di prestazioni. Ho trovato la tua soluzione un paio di volte qui su SO. E l'output di 'EXPLAIN' ha un bell'aspetto, quindi mi aspettavo che la query fosse molto veloce. Ma invece è stato molto più lento. Strano. – steffen

0

aggiungere come questo

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 è pseudo colonna, puoi provare la tua colonna restrittiva

Problemi correlati