web-dev-qa-db-ja.com

すべてのアカウントの最後のトランザクションに対してSQLクエリを作成するにはどうすればよいですか?

「acct_id」、「trans_date」、「trans_type」の列を持つテーブル「transactions」があり、このテーブルをフィルタリングして、各アカウントの最後のトランザクションだけを取得するとします。明らかに私は次のようなことができます

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

しかし、その後、trans_typeを失います。次に、日付とアカウントIDのリストを使用して2回目のSQL呼び出しを実行し、trans_typeを取り戻すことができますが、SQLサーバーとの間でデータを送受信するか、一時テーブルを作成することを意味するため、非常に扱いにくいと感じます。

単一のクエリでこれを行う方法はありますか?できれば、mysql、postgres、sql-server、およびOracleで機能する汎用メソッドです。

13
Steven Noble

これは greatest-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が特定のアカウントに対して一意であることを前提としています。そうでない場合、同点が発生し、クエリはすべての同点の行を返します。しかし、これは他の人々によって与えられたすべての解決策にも当てはまります。

私はほとんどの場合、GROUP BYをうまく最適化しないMySQLで作業するため、このソリューションを好みます。したがって、この外部結合ソリューションは通常、パフォーマンスに優れていることがわかります。

24
Bill Karwin

これはSQLServerで機能します...

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
)
11
David

これを試して

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
2
Raj More
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
1
RedFilter