2009-10-01 4 views
9

Dire che ho una tabella "transazioni" che ha colonne "acct_id" "trans_date" e "trans_type" e voglio filtrare questa tabella in modo da avere solo l'ultima transazione per ogni account. Chiaramente potrei fare qualcosa comeCome creare una query SQL per l'ultima transazione di ogni account?

SELECT acct_id, max(trans_date) as trans_date 
FROM transactions GROUP BY acct_id; 

ma poi ho perso il mio trans_type. Potrei quindi fare una seconda chiamata SQL con la mia lista di date e id di account e recuperare il mio trans_type ma che sembra molto cludgy dal momento che significa inviare i dati avanti e indietro al server sql o significa creare una tabella temporanea.

C'è un modo per farlo con una singola query, si spera che un metodo generico funzioni con mysql, postgres, sql-server e oracle.

risposta

17

Questo è un esempio di una query greatest-n-per-group. Questa domanda appare più volte alla settimana su StackOverflow. In aggiunta alle soluzioni subquery date da altre persone, ecco la mia soluzione preferita, che non utilizza subquery, GROUP BY, o CTE:

SELECT t1.* 
FROM transactions t1 
LEFT OUTER JOIN transactions t2 
    ON (t1.acct_id = t2.acct_id AND t1.trans_date < t2.trans_date) 
WHERE t2.acct_id IS NULL; 

In altre parole, restituire una riga tale che nessun altra riga esiste con lo stesso acct_id e un maggiore trans_date.

Questa soluzione presuppone che trans_date sia univoco per un determinato account, altrimenti potrebbero verificarsi dei legami e la query restituirà tutte le righe legate. Ma questo è vero per tutte le soluzioni date da altre persone.

Preferisco questa soluzione perché lavoro più spesso su MySQL, che non ottimizza molto bene lo GROUP BY. Quindi questa soluzione di unione esterna di solito si dimostra migliore per le prestazioni.

8

Questo funziona su SQL Server ...

SELECT acct_id, trans_date, trans_type 
FROM transactions a 
WHERE trans_date = (
    SELECT MAX(trans_date) 
    FROM transactions b 
    WHERE a.acct_id = b.acct_id 
) 
+1

Questo è il mio preferito. Puoi anche avere la tua clausola where 'NOT EXISTS (SELEZIONA NULL DALLA transazione z WHERE a.acct_id = z.acct_id AND z.trans_date> a.trans_date)' – cjk

1
select t.acct_id, t.trans_type, tm.trans_date 
from transactions t 
inner join (
    SELECT acct_id, max(trans_date) as trans_date 
    FROM transactions 
    GROUP BY acct_id; 
) tm on t.acct_id = tm.acct_id and t.trans_date = tm.trans_date 
+0

Errore fisso ... – RedFilter

+0

Ecco cosa ho risolto, a sinistra out trans_date nella clausola ON. – RedFilter

1

Prova questa

WITH 
LastTransaction AS 
(
    SELECT acct_id, max(trans_date) as trans_date 
    FROM transactions 
    GROUP BY acct_id 
), 
AllTransactions AS 
(
    SELECT acct_id, trans_date, trans_type 
    FROM transactions 
) 
SELECT * 
FROM AllTransactions 
INNER JOIN AllTransactions 
    ON AllTransactions.acct_id = LastTransaction.acct_id 
    AND AllTransactions.trans_date = LastTransaction.trans_date 
Problemi correlati