本文へ移動
ChatRound
  1. ChatRound
  2. 例
  3. コード

部署ごとに給与上位3つの金額の社員を全員取得:AIのSQLをチェック

Gemini 2.5 Flash-Lite のクエリは正しいとの判定。ただし対応バージョンとして書いた「SQLite 3.33.0+」は不正確で、3.25.0 から動きます。

例以下の回答は、このページのために Gemini 2.5 Flash-Lite に Google の API 経由で、ウェブ検索なしで書かせたものです。誰かの実際の会話ではありません。ChatRound が 2026年10月27日 にチェックしました。チェックしたのは、この会社の回答に ChatRound が標準で選ぶ2つのモデルです。

Gemini 2.5 Flash-Lite の回答 employees(id, name, department_id, salary) テーブルから、部署ごとに給与が上位3番目までの金額に入る社員を全員取得するSQLを書いてください。同じ給与の人が複数いたら全員含めてください。

はい、承知いたしました。部署ごとに給与が上位3番目までの社員を、同額の社員も全員含めて取得するSQLクエリは以下のようになります。

この問題は、ウィンドウ関数である DENSE_RANK() を使用するのが最も効率的かつ直感的な方法です。

WITH RankedEmployees AS (
    SELECT
        e.id,
        e.name,
        e.department_id,
        e.salary,
        DENSE_RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) as salary_rank
    FROM
        employees e
)
SELECT
    id,
    name,
    department_id,
    salary
FROM
    RankedEmployees
WHERE
    salary_rank <= 3
ORDER BY
    department_id, salary DESC;
クエリの説明
  1. WITH RankedEmployees AS (...) (Common Table Expression - CTE):

    • まず、RankedEmployees という名前の一時的な結果セット(CTE)を作成します。これにより、クエリが読みやすくなります。
    • SELECT e.id, e.name, e.department_id, e.salary, ... FROM employees e: employees テーブルから必要なカラムを選択します。
    • DENSE_RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) as salary_rank: ここが最も重要な部分です。
      • DENSE_RANK(): これはウィンドウ関数で、同じ値には同じランクを割り当て、ランクの飛び番をなくします。例えば、給与が 50000, 50000, 45000, 40000 という順序の場合、ランクは 1, 1, 2, 3 となります(RANK() の場合は 1, 1, 3, 4 となります)。
      • OVER (...): ウィンドウ関数が適用される「ウィンドウ」を定義します。
      • PARTITION BY e.department_id: データを department_id ごとに分割します。これにより、各部署内で独立してランク付けが行われます。
      • ORDER BY e.salary DESC: 各パーティション(部署)内で、給与(salary)を降順(高い順)に並べ替えます。
      • as salary_rank: 計算されたランクに salary_rank という別名を付けます。
  2. SELECT id, name, department_id, salary FROM RankedEmployees WHERE salary_rank <= 3:

    • CTE RankedEmployees から、salary_rank が 3 以下であるレコードのみを選択します。これにより、各部署で給与が上位3番目以内に入る社員(同額も含む)が取得されます。
  3. ORDER BY department_id, salary DESC:

    • 最終的な結果を、まず department_id で昇順に並べ、その中で salary で降順に並べることで、見やすくします。
なぜ DENSE_RANK() が適しているのか
  • 同額の社員を全員含める: DENSE_RANK() は、同じ給与の社員には同じランクを付与します。例えば、ある部署で給与が 50000, 50000, 45000, 40000 となっていた場合、DENSE_RANK() は 1, 1, 2, 3 となります。salary_rank <= 3 という条件により、給与 50000 の2名(ランク1)と給与 45000 の1名(ランク2)、給与 40000 の1名(ランク3)がすべて取得されます。
  • ランクの飛び番がない: DENSE_RANK() はランクの飛び番がないため、上位3番目までの「金額」という条件に正確に対応できます。

このクエリは、ほとんどの主要なSQLデータベース(MySQL 8.0+, PostgreSQL, SQL Server, Oracle, SQLite 3.33.0+ など)で動作します。

概ね信頼できますが、SQLiteの対応バージョン表記が不正確で、NULL給与の扱いにはDB差があります。

  • 1 誤り
  • 0 見解が分かれる
  • 4 問題なし
  • 2 見落とし

DeepSeek V4.1 Flash、Claude Haiku 4.5 がチェック · レポート作成:GPT-5.6 Luna

誤りのある箇所

誤り

SQLiteのウィンドウ関数は3.25.0で導入されているため、対応バージョンの下限を3.33.0とするのは不正確です。SQLite 3.25.0以降でこのクエリを実行できます。

Gemini 2.5 Flash-Lite
このクエリは、ほとんどの主要なSQLデータベース(MySQL 8.0+, PostgreSQL, SQL Server, Oracle, SQLite 3.33.0+ など)で動作します。
DeepSeek V4.1 Flash
“SQLite のウィンドウ関数は 3.25.0(2018年9月)で導入されており、3.25.0〜3.32.x でも動作します。”

そのほか

問題のない点4 件 ▾
  • 部署ごとに給与を降順で分割し、DENSE_RANK()で給与額の順位を付ける方針は正しいです。
  • 同額の社員を全員含める要件には、RANK()ではなくDENSE_RANK()が適しています。
  • CTEでウィンドウ関数を計算し、外側のWHERE句でsalary_rank <= 3を指定する構成は正しいです。
  • 提示されたSQLは、通常のNULLでない給与データに対して要件どおり動作します。
見落としている点2 件 ▾
  • salaryがNULLの場合、ORDER BY salary DESCにおけるNULLの順位はデータベースによって異なります。PostgreSQLではNULLが先頭、MySQLでは末尾になるため、NULLを除外するか位置を明示する注意書きが必要です。
  • MySQL 5.7以前などウィンドウ関数を使えない環境向けの代替SQLには触れていません。
1つのモデルだけが指摘1 件 ▾
  • DeepSeek V4.1 Flash: department_idがNULLの行は同じ1つのパーティションにまとめられるため、NULL部署を別扱いしたい場合は仕様を明確にする必要があります。 有益な視点

修正した回答

部署ごとに給与額の上位3種類に入る社員を、同額の社員も全員含めて取得するには、DENSE_RANK()を使います。

WITH RankedEmployees AS (
    SELECT
        e.id,
        e.name,
        e.department_id,
        e.salary,
        DENSE_RANK() OVER (
            PARTITION BY e.department_id
            ORDER BY e.salary DESC
        ) AS salary_rank
    FROM employees e
)
SELECT
    id,
    name,
    department_id,
    salary
FROM RankedEmployees
WHERE salary_rank <= 3
ORDER BY department_id, salary DESC;

DENSE_RANK()は同じ給与に同じ順位を付け、順位の飛び番を作りません。例えば給与が 50000, 50000, 45000, 40000 の場合、順位は 1, 1, 2, 3 になります。そのため、salary_rank <= 3で給与額の上位3種類に該当する社員を、同額の人も含めて取得できます。

RANK()では同じ給与の人数に応じて順位が飛ぶため、給与額の上位3種類を取得する要件には通常DENSE_RANK()が適しています。

このクエリは、MySQL 8.0以降、PostgreSQL、SQL Server、Oracle、SQLite 3.25.0以降など、ウィンドウ関数に対応するデータベースで利用できます。MySQL 5.7以前などでは、ウィンドウ関数を使わない相関サブクエリなどの代替が必要です。

なお、salaryにNULLが含まれる場合、ORDER BY salary DESCでのNULLの位置はデータベースによって異なります。NULLを上位3種類の対象外にする場合は、例えば次のように明示できます。

WHERE salary IS NOT NULL
  AND salary_rank <= 3

また、department_idがNULLの行は、通常は同じパーティションとしてまとめて順位付けされます。

編集部の確認

公開前に、このレポートを読んで確認しました

SQLiteの公式ドキュメントでも、ウィンドウ関数は3.25.0で追加されています。給与が 50000, 50000, 45000, 40000 のデータで実行すると、4人全員が返りました。

2026年10月28日 に下記の資料と照らし合わせて確認。

出典:

各モデルのチェック全文

DeepSeek V4.1 Flash▾

1. 私の答え

要件(部署ごとに「給与の金額」上位3種類に入る社員を、同額は全員含める)に対しては DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) で <= 3 を取るのが正解で、提示されたクエリはそのまま動きます。RANK() だと同額の人数分だけ順位が飛んで「3番目の金額」が漏れるため不適切、という点も含めて方針は正しいです。私が付け足すとすれば、NULL 給与の扱い(DBによって降順時の NULL の位置が逆になる)と、対応バージョンの最小値の正確さくらいです。

2. 主張ごとの検証

  • DENSE_RANK() を使う方針 → holds。要件の「金額ベースで上位3番目まで」に正確に対応します。
  • 同額は同順位・飛び番なし(例: 50000, 50000, 45000, 40000 → 1, 1, 2, 3) → holds。RANK() なら 1, 1, 3, 4 になるという対比も正確です。
  • クエリ本体(CTE → salary_rank <= 3 → ORDER BY department_id, salary DESC) → holds。構文・論理とも問題なく、同額の社員も全員返ります。
  • 「各部署で給与が上位3番目以内に入る社員(同額も含む)が取得されます」 → holds。
  • 対応 DB の記述「このクエリは、ほとんどの主要なSQLデータベース(MySQL 8.0+, PostgreSQL, SQL Server, Oracle, SQLite 3.33.0+ など)で動作します。」 → 一部 誤り(軽微)。MySQL 8.0+、SQL Server、Oracle、PostgreSQL は正しいですが、SQLite のウィンドウ関数は 3.25.0(2018年9月)で導入されており、3.25.0〜3.32.x でも動作します。「3.33.0+」と書くと最小要件を実際より1つ以上高く見積もったことになります(動作するのは事実なので実害は小さいですが、前提として書くなら 3.25.0+ が正しい)。

3. 見落としている点

  • salary が NULL の場合の挙動。PostgreSQL は ORDER BY salary DESC で NULL が先頭(ランク1)に来るため、NULL 給与の社員が「上位3金額」に混入します。MySQL では逆に末尾に来ます。DB をまたぐなら ORDER BY (salary IS NULL), salary DESC などで明示すべきです。この注意がありません。
  • ウィンドウ関数が使えない環境(MySQL 5.7 など)の代替。バージョン互換に触れている以上、COUNT(DISTINCT ...) を使う相関サブクエリ版を一言添えると親切ですが、必須ではありません。
  • department_id が NULL の行が1つのパーティションにまとめられる点は、仕様として許容かどうか一言あるとより親切です。

4. 修正版(該当箇所のみ)

対応バージョンの記述を次のように置き換えます。

このクエリは、ほとんどの主要なSQLデータベース(MySQL 8.0+, PostgreSQL, SQL Server 2005+, Oracle, SQLite 3.25.0+ など)で動作します。MySQL 5.7 以前などウィンドウ関数がない環境では、COUNT(DISTINCT salary) による相関サブクエリで代替できます。

末尾に次の注意を追加します。

注意(NULL 給与): salary に NULL があり得る場合、ORDER BY salary DESC の NULL の位置は DB によって異なります(PostgreSQL では先頭=ランク1、MySQL では末尾)。NULL を除外または最後尾に固定したい場合は、ORDER BY (salary IS NULL), salary DESC のように明示してください。

上記以外(方針、DENSE_RANK() の説明、クエリ本体)はそのままで問題ありません。

Claude Haiku 4.5▾

レビュー

自分の答え

部署ごとに給与が上位3番目までの社員を同額も含めて取得するには、DENSE_RANK() ウィンドウ関数を使う方法が正しいアプローチです。CTEで先にランク付けし、外側のWHERE句でランク≤3をフィルタリングするのが標準的な解法です。

クレームごとの検証

ウィンドウ関数の選択 (DENSE_RANK()) — 成立 提示されたクエリと説明は正確です。learnsql.comとdatadank.comの両方で、同じ給与の複数人を全員含める場合はDENSE_RANK()が推奨されています。

RANK() vs DENSE_RANK() の説明 — 成立 「RANK() の場合は 1, 1, 3, 4 となります」という説明は正確です。datadank.comの例でも同じ動作が示されています。

CTE内でのウィンドウ関数、外側でのWHEREフィルタリング — 成立 「ウィンドウ関数はWHERE句では使えないため、CTEで先に計算し、外側のクエリでフィルタリングする」という説明と実装は正しいです。datadank.comでも「window functions are not allowed in WHERE」と明記されています。

PARTITION BY department_id と ORDER BY salary DESC — 成立 各部署内で独立してランク付けし、給与を降順にするという設定は正確です。

salary_rank <= 3 による絞り込み — 成立 salary_rank <= 3 で上位3番目までが取得されることの説明も正確です。

見落とし

特段重要な見落としはありません。ただし、以下の補足があれば、より完全だったでしょう:

  1. 複数の同じランクの場合の具体例 — 「給与が 50000, 50000, 45000, 40000 となっていた場合」の例は示されていますが、さらに「給与が 50000, 50000, 50000, 45000, 40000 の場合、salary_rank <= 3 でもランク1の50000は全員含まれるが、ランク4の40000は含まれない」といった例があると、より明確だったかもしれません。

  2. 他のウィンドウ関数との比較表 — ROW_NUMBER() や RANK() を使う場合との結果の違いを表で示すと、なぜDENSE_RANK()が適切かがより際立つでしょう。

修正内容

この回答は正確です。修正は不要です。

参照したページ:LeetCode SQL Problem & Solution: Department Top Three Salaries | LearnSQL.com · How to Find the Nth-Highest Salary by Department with SQL | LearnSQL.com · Find the top three highest-paid employees in each department · Top N per Group in SQL: Top 3 Salaries per Dept | DataDank · Top-N / Ranking · Data Engineering Interview Prep: The Complete Course | Data Vidhya

次はあなたの番

その回答を使う前に、チェックを。

ChatGPT、Claude、Gemini など、どの AI の回答でも貼り付けてください。ほかの会社の AI 2つが確認し、誤りを示します。

回答をチェック Chrome 拡張機能を追加

ChatGPT、Claude、Gemini のすべての回答の下に「チェック」ボタンが付きます。

ほかのチェック例