SQL Serverでグループごとの最大値のレコードを取得する方法を解説!

スポンサーリンク

SQL Serverでデータを扱う際、特定のグループごとに最大値を持つレコードを取得したい場面があります。例えば、各顧客の最新の注文や、部署ごとの最高給与の社員情報を取得する場合などです。

このようなケースでは、GROUP BYMAX() を使うだけでは、最大値の「値」は取得できても、その値を持つレコード全体を簡単には取得できません。

本記事では、SQL Serverで「グループごとの最大値を持つレコード全体」を取得する方法について、いくつかのアプローチを紹介します。それぞれの手法のメリット・デメリットも解説するので、用途に応じて最適な方法を選べるようになります。

それでは、実際の方法を見ていきましょう!

JOIN を使った方法

GROUP BYMAX() では、最大値の「値」は取得できますが、どのレコードがその値を持っているのかまではわかりません。そこで、JOIN を使って、最大値を持つレコード全体を取得する方法を紹介します。

以下のEmployeesテーブルを例に考えてみましょう。

EmployeeIDNameDepartmentSalary
1AliceHR50000
2BobHR60000
3CharlieIT70000
4DavidIT90000
5EveSales55000

JOIN を使って最大値のレコードを取得

各部署ごとに最高給与を持つ従業員の情報を取得するには、以下のようにJOIN を活用します。

SELECT e.EmployeeID, e.Name, e.Department, e.Salary
FROM Employees e
INNER JOIN (
    SELECT Department, MAX(Salary) AS MaxSalary
    FROM Employees
    GROUP BY Department
) max_salaries
ON e.Department = max_salaries.Department AND e.Salary = max_salaries.MaxSalary;

実行結果

EmployeeIDNameDepartmentSalary
2BobHR60000
4DavidIT90000
5EveSales55000
  1. サブクエリ (max_salaries) で各部署の最大給与を取得
    • GROUP BY Department を使用して、各部署ごとの最大給与を計算
  2. メインクエリで INNER JOIN を使用
    • Employees テーブルと max_salaries を結合し、SalaryMaxSalary と一致するレコードのみを取得

この方法はシンプルで分かりやすく、最大値のレコード全体を取得するのに便利です。ただし、同じ最大値を持つ複数のレコードが存在する場合、それらもすべて取得される点に注意が必要です。

次のセクションでは、ROW_NUMBER() を使ってグループごとの最大値のレコードを取得する方法を紹介します。

WITH(CTE)とROW_NUMBER() を使った方法

JOIN を使った方法では、最大値を持つレコードを取得できますが、同じ最大値を持つ複数のレコードが存在する場合、それらがすべて取得されてしまいます。一方、ROW_NUMBER() を使えば、各グループごとに最大値を持つレコードを1件だけ取得することが可能です。

再び、以下のEmployees テーブルを例に考えます。

EmployeeIDNameDepartmentSalary
1AliceHR50000
2BobHR60000
3CharlieIT70000
4DavidIT90000
5EveSales55000

ROW_NUMBER()を使ってグループごとの最大レコードを取得

以下のSQLで、各部署の最大給与を持つ従業員の情報を1件だけ取得できます。

WITH RankedEmployees AS (
    SELECT 
        EmployeeID, 
        Name, 
        Department, 
        Salary, 
        ROW_NUMBER() OVER (PARTITION BY Department ORDER BY Salary DESC) AS RowNum
    FROM Employees
)
SELECT EmployeeID, Name, Department, Salary
FROM RankedEmployees
WHERE RowNum = 1;

実行結果

EmployeeIDNameDepartmentSalary
2BobHR60000
4DavidIT90000
5EveSales55000
  1. WITH を使って共通テーブル式(CTE)を作成
    • ROW_NUMBER() を使用して、各 Department ごとに Salary の降順で並べ、各行に RowNum を割り当てる
  2. WHERE RowNum = 1 で最大値のレコードのみ取得
    • Department について、RowNum = 1 のレコードだけを選択

メリット

  • 各グループごとの最大値を持つレコードを「1 件だけ」取得できる
  • ORDER BY の条件を変えれば、最小値や他の基準に基づくレコードも簡単に取得可能

⚠️ デメリット

  • WITH(CTE)を使うため、SQL に慣れていないと少し分かりづらい
  • ROW_NUMBER() の計算コストが発生するため、大量データではパフォーマンスに注意

次のセクションでは、CROSS APPLY を使った方法を紹介します!

CROSS APPLY を使った方法

CROSS APPLY を使うことで、各グループごとの最大値を持つレコードを効率よく取得できます。特に、インデックスが適切に設定されている場合、JOIN よりもパフォーマンスが向上することがあります。

再び、以下のEmployees テーブルを使用します。

EmployeeIDNameDepartmentSalary
1AliceHR50000
2BobHR60000
3CharlieIT70000
4DavidIT90000
5EveSales55000

CROSS APPLY を使って最大値のレコードを取得

SELECT e.EmployeeID, e.Name, e.Department, e.Salary
FROM Departments d
CROSS APPLY (
    SELECT TOP 1 EmployeeID, Name, Department, Salary
    FROM Employees e
    WHERE e.Department = d.Department
    ORDER BY Salary DESC
) e;

実行結果

EmployeeIDNameDepartmentSalary
2BobHR60000
4DavidIT90000
5EveSales55000
  1. CROSS APPLY を使用してサブクエリを実行
    • Departments テーブルの各レコードに対して、Employees テーブルから Department が一致するレコードを取得
    • TOP 1Salary DESC 順に並べ、最大給与のレコードのみを取得
  2. ORDER BY Salary DESC を指定
    • Department 内で給与が最も高い従業員を取得

メリット

  • TOP 1 により、各グループの最大レコードを効率的に取得
  • CROSS APPLYJOIN よりも柔軟で、特定の条件を追加しやすい
  • 適切なインデックスを設定すれば、高速に動作

⚠️ デメリット

  • CROSS APPLY の概念が初心者には少し分かりづらい
  • Departments テーブルが必要(もし Employees テーブル単体で使いたい場合は別の工夫が必要)

まとめ

本記事では、SQL Server で「グループごとの最大値を持つレコードを取得する方法」について解説しました。

どの方法を選ぶべきか?

  • 「最大値のレコードをすべて取得」したい場合JOIN + MAX()
  • 「各グループごとに 1 件だけ取得」したい場合ROW_NUMBER()
  • 「パフォーマンスを考慮しつつ効率的に取得」したい場合CROSS APPLY(インデックス推奨)

SQL の選択肢は多く、データの特性や実行環境によって最適な方法は異なります。用途に応じて適切な方法を選び、パフォーマンスを考慮しながら運用しましょう。

本記事が参考になれば幸いです。ご質問や他の SQL に関する疑問があれば、ぜひコメントでお知らせください!

SQL Server
スポンサーリンク
なんくるをフォローする

コメント

タイトルとURLをコピーしました