2012-12-04 13 views
21

Ho due tabelle (membri e attività) e sto cercando di interrogare i membri con l'ultima attività per ogni membro. Ho lavorato con due query (una per ottenere i membri e una seconda con un massimo (id) e un gruppo per (membro) sulle attività) e un codice per unire i dati. Sono sicuro che può essere fatto con una singola query, ma non riesco a capirlo. Qualche idea?MySQL LEFT JOIN utilizza MAX & GROUP BY nella tabella unita?

membri tabella tavolo

id, name 
1, Shawn 
2, bob 
3, tom 

attività

id, member_id, code, timestamp, description 
1,   1, 123,  15000, baked a cake 
2,   1, 456,  20000, ate dinner 
3,   2, 789,  21000, drove home 
4,   1, 012,  22000, ate dessert 

risultato desiderato:

id, name, activity_code, activity_timestamp, activity_description 
1, shawn, 012,   22000,    ate dessert 
2, bob, 789,   21000,    drove home 
3, tom, null,   null,    null 

risposta

26

L' Il problema "ultimo per gruppo" è estremamente comune in SQL. Ci sono innumerevoli esempi di soluzioni a questo stesso problema su questo sito da solo.

Se i timestamp sono uniqe per attività utente:

SELECT 
    m.id, 
    m.name, 
    a.code activity_code, 
    a.timestamp activity_timestamp, 
    a.description activity_description 
FROM 
    members m 
    INNER JOIN activities a ON a.member_id = m.id 
WHERE 
    a.timestamp = (SELECT MAX(timestamp) FROM activities WHERE member_id = m.id) 

In alternativa, se l'ID attività sta aumentando in modo monotono con il tempo:

... 
WHERE 
    a.id = (SELECT MAX(id) FROM activities WHERE member_id = m.id) 

Non è necessario al gruppo. Ma la query trarrà vantaggio da un indice su activities su (member_id, timestamp) o (member_id, id), rispettivamente.


EDIT

Per mostrare tutti i membri che non hanno effettuato un'attività, utilizzare un LEFT JOIN come questo.

SELECT 
    m.id, 
    m.name, 
    a.code activity_code, 
    a.timestamp activity_timestamp, 
    a.description activity_description 
FROM 
    members m 
    LEFT JOIN activities a ON 
    a.member_id = m.id 
    AND a.timestamp = (SELECT MAX(timestamp) FROM activities WHERE member_id = m.id) 

Si noti che non v'è alcuna WHERE clausola. Semanticamente, DOVE viene applicato dopo i join sono terminati. Quindi una clausola WHERE rimuoveva le righe aggiunte da LEFT JOIN, dando effettivamente lo stesso risultato dell'originale INNER JOIN.

Ma se si applica il predicato aggiuntivo a destra nella condizione di join, LEFT JOIN funzionerà come previsto.

+0

Sembra che il mio membro (tom, id = 3) non abbia mai registrato alcuna attività. Ho provato a cambiare INNER JOIN in LEFT JOIN. Sto provando la seconda istruzione WHERE, i miei timestamp non sono garantiti come univoci, ma l'ID è un campo di autoincremento unico. –

+0

@ShawnMcBride Vedi risposta modificata. – Tomalak

+0

Personalmente non mi piacciono le query annidate in condizioni. Sarebbe una soluzione molto più ottimizzata se ti unisci alla query che ha solo timestamp MAX, come la risposta sotto – dzona

1
Select max(a.id), m.name, a.activity_code, a.activity_timestamp, a.activity_description 
From members m 
    Left join 
    activities a on a.member_id=m.id 
Group by m.name, a.activity_code, a.activity_timestamp, a.activity_description 
6
SELECT 
    members.id , 
    members.name, 
    activities.code AS activity_code, 
    activities.timestamp AS activity_timestamp, 
    activities.description AS activity_description 
FROM 
    members 
    LEFT JOIN activities 
     ON members.id = activities.member_id 
    LEFT JOIN 
     (
      SELECT 
       activities.member_id 
       MAX(activities.id) AS id 
      FROM activities 
      GROUP BY 
       activities.member_id 
     ) AS t1 
     ON activities.id = t1.id 
WHERE 
    t1.id IS NOT NULL 
+0

Penso che dovresti usare un join sinistro in entrambi i casi. – Tomalak

+0

Sembra che il mio membro non stia registrando alcuna attività. Ottengo lo stesso risultato della domanda di Tomalak. Se cambio il secondo JOIN in un LEFT JOIN sembra che stia tornando tutti i record di attività. –

+0

@ShawnMcBride 'DOVE mancava t1.id IS NOT NULL'. Per favore, prova di nuovo. – edze