PostgreSQLでグループごとの最大値を取得する方法【DISTINCT ON】

※ この記事にはアフィリエイトリンクが含まれます

PostgreSQLで「グループごとの最大値」を取得しようとしたとき、こんな悩みはありませんか?

  • MAXは使えるけど、他のカラムが取得できない
  • GROUP BYを使うとエラーになる
  • 最新レコードを1件だけ取りたい

結論から言うと、PostgreSQLでは以下の方法で解決できます。

  • 最大値だけ取得
    MAX + GROUP BY
  • 最大値の行を取得
    DISTINCT ON
  • 柔軟に制御したい(最大値から2番目だけを取得したい場合など)
    ROW_NUMBER()

特に、「グループごとに1件だけ取得したい場合」はDISTINCT ONが最もシンプルで実務向きです。

本記事では、それぞれの方法を初心者にもわかりやすく解説しつつ、実務でよく使う「最新レコード取得」のパターンまで完全に理解できる内容になっているかと思いますので、ぜひ最後までご覧ください。

目次

グループごとの最大値とは?

「グループごとの最大値」とは、特定の単位ごとに最大の値を取得することです。

例えば、以下のようなケースがあります。

  • 部署ごとの最高給与
  • 商品ごとの最新データ
  • ユーザーごとの最終ログイン

ここで多くの人がつまずくのが、「最大値は取れるけど、その行の他のカラムが取れない」問題です。

【基本】MAX + GROUP BYで最大値を取得

まずは基本の書き方です。

SELECT department_id, MAX(salary) AS max_salary
FROM employees
GROUP BY department_id;

このSQLでは、部署ごとに最大給与を取得できます。

ポイント

  • グループ単位で最大値を取得できる
  • シンプルで理解しやすい

デメリット

  • その最大値を持つ「行」は取得できない

例えば、「最高給与となっている社員の名前」までは取得できません。

【よくあるミス】MAXで他のカラムを取ろうとする

初心者がよくやるミスがこちらです。

SELECT department_id, name, MAX(salary)
FROM employees
GROUP BY department_id;

これはエラーになります。

なぜエラーになるのか?

GROUP BYでは、集約されていないカラム(上記の場合だとname)をSELECTに含めることができないためです。

つまり、「最大値の行そのもの」を取得するには別の方法が必要になります。

【実務で最強】DISTINCT ONで最大行を取得

PostgreSQLで最もおすすめなのがDISTINCT ON です。

SELECT DISTINCT ON (department_id)
    department_id,
    name,
    salary
FROM employees
ORDER BY department_id, salary DESC;

特徴

  • PostgreSQL専用の機能
  • 記述がシンプル
  • パフォーマンスがいいことが多い

重要ポイント

ORDER BY の書き方で「どの行を残すか」が決まります。

ORDER BY department_id, salary DESC;

とすることで、「各部署で給与が最も高い行」を取得できます。

実務での使いどころ

  • 最新レコード取得
  • 重複データの整理
  • グループごとの代表行取得

【汎用性◎】ROW_NUMBER()で最大行を取得

次に紹介するのが、ウィンドウ関数ROW_NUMBER() を使う方法です。

SELECT *
FROM (
    SELECT *,
           ROW_NUMBER() OVER (
               PARTITION BY department_id
               ORDER BY salary DESC
           ) AS rn
    FROM employees
) sub
WHERE rn = 1;

特徴

  • ほぼすべてのDBで使える
  • 柔軟な条件設定が可能

メリット

  • 2位・3位も取得できる
  • 条件を細かく制御できる

例えば「上位3件」を取得することも可能です。

WHERE rn <= 3;

デメリット

  • DISTINCT ON よりやや複雑
  • 大量データでは遅くなる場合がある

DISTINCT ONとROW_NUMBERの使い分け

実務では、以下のように使い分けるのがおすすめです。

  • シンプルに1件取得 → DISTINCT ON
  • 順位づけや複雑条件がある → ROW_NUMBER
  • 最大値だけ取得したい → MAX + GROUP BY

パフォーマンスを意識するなら

グループごとの最大値取得は、データ量が増えるとパフォーマンスに影響します。

そのため、インデックスの設定が重要です。

CREATE INDEX idx_emp_dept_salary
ON employees (department_id, salary DESC);

ポイント

  • GROUP BYORDER BYに使うカラムにインデックスを張る
  • 複合インデックスを意識する

これだけで、クエリ速度が大きく改善することもあります。

【応用】最新レコードを取得する(実務で超重要)

実務では「最大値」よりも「最新レコード」を取得するケースが非常に多いです。

SELECT DISTINCT ON (user_id)
    user_id,
    created_at,
    status
FROM logs
ORDER BY user_id, created_at DESC;

ポイント

  • created_at DESC で最新を取得
  • グループごとに1件だけ取得

このパターンはかなり使うので、覚えておくと即戦力になります。

まとめ

PostgreSQLでグループごとの最大値を取得する方法は以下の通りです。

  • 最大値のみ → MAX + GROUP BY
  • 最大値の行 → DISTINCT ON
  • 柔軟な制御 → ROW_NUMBER()

特に重要なのは、「何を取得したいか」で使い分けることです。

迷った場合は、まずは DISTINCT ON を使ってみるとよいでしょう。


PostgreSQLは、現場でも広く使われている信頼性の高いデータベースです。もしこれから本格的に学び、実務で通用する力をつけたい方には、RareTECHをチェックしてみてください。実案件ベースのカリキュラムで、あなたのスキルを次のステージへ引き上げてくれるはずです。


なんくる

「本当にエンジニアとしてやっていけるか不安…」という方も、実践的な開発に関わることで、転職後の働き方を事前に体感できますよ。

実務で使えるDBスキルとともに、プログラミングスキルをちゃんと身につけたいなら、
RareTECH無料カウンセリングで、学ぶ目的やゴールをプロと一緒に明確にしてみましょう。独学では得られない「実践的な成長の道筋」が見えてきます。


もしこの内容を通して、PostgreSQLについてさらに理解を深めたいと感じられたなら、信頼できる講座や書籍を紹介した別記事をご覧いただくのも良いかと思います。ご自身の学びに、きっとお役立ていただけるはずです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

沖縄出身のエンジニアです。IT業界で5年以上の経験があり、主にC#やPHPを使って開発を行ってきました。新しい技術にも興味があり、日々学びながらスキルアップを目指しています。

コメント

コメントする

CAPTCHA


目次