Как сделать SQL-запрос для последней транзакции каждой учетной записи? - PullRequest
11 голосов
/ 01 октября 2009

Допустим, у меня есть таблица «транзакции», в которой есть столбцы «acct_id», «trans_date» и «trans_type», и я хочу отфильтровать эту таблицу, чтобы у меня была только последняя транзакция для каждой учетной записи. Ясно, что я мог бы сделать что-то вроде

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

но тогда я теряю свой trans_type. Затем я мог бы сделать второй SQL-вызов со своим списком дат и идентификаторами учетных записей и получить свой trans_type обратно, но это выглядит очень неудобно, поскольку это означает либо отправку данных назад и вперед на сервер sql, либо это означает создание временной таблицы.

Есть ли способ сделать это с помощью одного запроса, надеюсь, универсальный метод, который будет работать с mysql, postgres, sql-server и oracle.

Ответы [ 4 ]

23 голосов
/ 01 октября 2009

Это пример запроса great-n-per-group . Этот вопрос возникает несколько раз в неделю на StackOverflow. В дополнение к решениям подзапросов, предоставленным другими людьми, вот мое предпочтительное решение, которое не использует подзапрос, GROUP BY или 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;

Другими словами, вернуть строку так, чтобы не было другой строки с таким же acct_id и большим trans_date.

В этом решении предполагается, что trans_date является уникальным для данной учетной записи, в противном случае могут возникнуть связи, и запрос вернет все связанные строки. Но это верно и для всех решений, данных другими людьми.

Я предпочитаю это решение, потому что я чаще всего работаю над MySQL, который не очень хорошо оптимизирует GROUP BY. Таким образом, это решение для внешнего соединения обычно оказывается лучше для производительности.

11 голосов
/ 01 октября 2009

Это работает на 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
)
2 голосов
/ 01 октября 2009

Попробуйте это

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
1 голос
/ 01 октября 2009
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
...