グループごとの最新レコードを取得するSQL
仕事でグループごとの最新レコードを取得するSQLを書く機会がありました。
グループごとの最新レコードを取得する方法は色々ありますが、今回は分析関数(ROW_NUMBER)を使用してみました。
グループごとの最新レコードを取得する方法
調べてみるとグループごとの最新レコードを取得する方法は色々あるようです。
特に参考になったのは以下です。
集約関数(MAX)やNOT EXISTSを使う方法
同一グループの中で最大のレコードを取得する SQL を書く - TIM Labs
GROUP BYや分析関数(ROW_NUMBER)を使う方法
SQLでグループごとにある最大値の行を取得する - Qiita
分析関数とは
分析関数を使う方法がありましたが、そもそも分析関数に馴染みがなかったので調べました。
分析関数(ROW_NUMBER・RANK・DENSE_RANK)は連番や順位を返すSQL関数です。
JOIN・WHERE・GROUP BY・HAVING句が実行された結果に対して分析関数が実行されるので、分析関数が使える箇所はSELECT・ORDER BY句になります。
分析関数によって取得レコードが集約されることはありません。
分析関数の構文は以下になります。
分析関数 OVER (PARTITION BY 集計単位 ORDER BY 表示順)
ROW_NUMBER()
1から始まる連番を返します。
同値の場合の順番は不定です。
順番を一定にしたい場合はソート条件にユニークキーを追加する必要があります。
RANK()
1から始まる順位を返します。
同値の場合は同順位で、次の順位は飛ばされます。
DENSE_RANK()
1から始まる順位を返します。
同値の場合は同順位で、次の順位は飛ばされません。
OVER句
分析関数や集約関数で使用できます。
PARTITION BY句で集計単位を指定します。
ORDER BY句でソート順を指定します。
PARTITION BY・ORDER BY句はそれぞれ省略可能で、OVER句自体も省略可能です。
分析関数でSQLを作成してみた
今回作成したSQLの仕様は以下です。
- グループごとで更新日時が最新のレコードを抽出する
- グルーピングするキーは2つある
- 更新日時はユニークではない
- 更新日時が同値のレコードが複数存在する場合、その中で任意のレコードを抽出する
グループキーが複数あり、最新レコードも複数存在しうるケースだったため、分析関数を使うのが簡単かと思いました。
とういうか、グループごとの最新レコードを取得したい場合は分析関数を使うのが一番楽な気がします。
上記の仕様を実装したSQLのサンプルが以下になります。
データベースはOracleを使用しています。
SELECT 順位付けしたテーブル.取得したいカラム1 , 順位付けしたテーブル.取得したいカラム2 , 順位付けしたテーブル.取得したいカラム3 FROM ( SELECT 取得したいカラム1 , 取得したいカラム2 , 取得したいカラム3 , ROW_NUMBER() OVER ( PARTITION BY 集計したいカラム1 , 集計したいカラム2 ORDER BY 更新日時 DESC ) AS 順位 FROM 対象テーブル WHERE 検索したいカラム1 = '検索条件1' AND 検索したいカラム2 = '検索条件2' AND 検索したいカラム3 = '検索条件3' ) 順位付けしたテーブル WHERE 順位付けしたテーブル.順位 = 1
参考
ROW_NUMBER,RANK,DENSE_RANK 行番号や順位を返すSQL関数
分析関数 ROW_NUMBER の使用例 - オラクル・Oracleをマスターするための基本と仕組み
SQL PARTITION BYの基本と効率的に集計する便利な方法
SQL - グルーピング, 集約関数
https://creasys.org/rambo/articles/9a3caec10b8c21e45671