2009-10-01 11 views
9

Digamos que tengo una tabla de "transacciones" que tiene columnas "acct_id" "trans_date" y "trans_type" y quiero filtrar esta tabla para que tenga la última transacción para cada cuenta. Es evidente que lo que podía hacer algo así como¿Cómo hacer una consulta SQL para la última transacción de cada cuenta?

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

pero luego perder mi trans_type. Podría hacer una segunda llamada SQL con mi lista de fechas y las identificaciones de cuenta y recuperar mi trans_type, pero eso me parece muy complicado, ya que significa enviar datos de ida y vuelta al servidor sql o crear una tabla temporal.

Hay una manera de hacer esto con una sola consulta, con suerte, un método genérico que funcionaría con mysql, postgres, sql-server y oracle.

Respuesta

17

Este es un ejemplo de una consulta greatest-n-per-group. Esta pregunta aparece varias veces por semana en StackOverflow. Además de las soluciones subconsulta dadas por otras personas, aquí está mi solución preferida, que utiliza sin subconsulta, 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; 

En otras palabras, devuelva una fila de modo que ninguna otra fila existe con el mismo acct_id y un mayor trans_date.

Esta solución asume que trans_date es único para una cuenta determinada, de lo contrario se pueden producir vínculos y la consulta devolverá todas las filas atadas. Pero esto es cierto para todas las soluciones dadas por otras personas también.

Prefiero esta solución porque la mayoría de las veces trabajo en MySQL, que no optimiza muy bien GROUP BY. Entonces, esta solución de combinación externa suele ser mejor para el rendimiento.

8

Esto funciona en 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

Este es mi favorito. También puede hacer que su cláusula where sea 'NOT EXISTS (SELECT NULL FROM transaction z DONDE 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

Error fijo ... – RedFilter

+0

Eso es lo que arreglé, dejado out trans_date en la cláusula ON. – RedFilter

1

probar este

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 
Cuestiones relacionadas