###前提・実現したいこと
使用DB PostgreSQL9.1.23
以下のテーブルについてSQLによるデータ抽出を行おうとしています。
サンプルテーブル(sample_tbl)
|フィールド名|gid|group_name|user_name|ctime|data1|
|:--|:--|:--|:--|:--|
|データ型|bigint|text|text|text|smallint|
|UNIQUE制約|○|×|×|×|×|
|インデックス|-|btree|btree|btree|btree|
■条件
- テーブルの設計は変更できません
- ctimeはtext型ですが、日時データが格納されています フォーマット:yyyy-mm-dd hh24:mi:ss.MS
- group_nameとuser_nameの組み合わせごとにctimeが最新のレコードを抽出します
- ctimeが最新のレコードが複数ある場合、gidが大きいレコードを対象とします
- 抽出したレコードをgroup_nameごとに集計し、各group_nameのdata1の合計値を結果として求めます
■データサンプル
|gid|group_name|user_name|ctime|data1|
|:--|:--|:--|:--|
|1|aaa|xxx|2017-06-24 12:00:00.001|3|
|2|aaa|xxx|2017-06-24 12:00:00.002|4|
|3|aaa|xxx|2017-06-24 12:00:00.002|1|
|4|aaa|yyy|2017-06-24 12:00:00.002|3|
|5|bbb|xxx|2017-06-24 12:00:00.001|3|
|6||xxx|2017-06-24 12:00:00.001|3|
###実装したSQL
SQL
1SELECT 2 group_name 3 , sum(data1) 4 FROM 5 sample_tbl 6 INNER JOIN ( 7 SELECT 8 max(sample_tbl.gid) AS gid 9 FROM 10 sample_tbl 11 INNER JOIN ( 12 SELECT 13 group_name 14 , user_name 15 , max(to_timestamp(ctime, 'yyyy-mm-dd hh24:mi:ss.MS')) AS ctime 16 FROM 17 sample_tbl 18 GROUP BY 19 group_name 20 , user_name 21 ) AS latest_id_tbl 22 ON to_timestamp(sample_tbl.ctime, 'yyyy-mm-dd hh24:mi:ss.MS') = latest_id_tbl.ctime 23 AND sample_tbl.group_name = latest_id_tbl.group_name 24 AND sample_tbl.user_name = latest_id_tbl.user_name 25 GROUP BY 26 sample_tbl.group_name 27 , sample_tbl.user_name 28 ) AS gid_tbl 29 ON sample_tbl.gid = gid_tbl.gid 30 GROUP BY 31 sample_tbl.group_name
###アドバイスいただきたいこと
SQL初学者であるため、正しく抽出できているのか、組立てに問題がないか自信がありません。
サブクエリがネストしており、パフォーマンスに不安があります。
修正すべき箇所、より優れた書き方等ありましたら、ご指摘をお願いいたします。
###追記1
アドバイスいただき、window関数のrankを使用して、以下の通り書き換えてみました。
誤りや改善の余地がありましたら、ご指摘いただけると幸いです。
SQL
1SELECT 2 group_name 3 , sum(data1) 4 FROM 5 ( 6 SELECT 7 * 8 , rank() OVER ( 9 PARTITION BY 10 group_name 11 , user_name 12 ORDER BY 13 to_timestamp(ctime, 'yyyy-mm-dd hh24:mi:ss.MS') DESC 14 , gid DESC 15 ) 16 FROM 17 sample_tbl 18 ) AS rank_tbl 19 WHERE 20 rank = 1 21 GROUP BY 22 group_name
回答4件