📘

SQL_MODE=ONLY_FULL_GROUP_BYのエラーとROW_NUMBERを使った解決策

2024/05/02に公開

はじめに

皆さんは、MySQL5.7以降のデフォルトSQL_MODEであるONLY_FULL_GROUP_BYが有効になっていることをご存知でしょうか?

https://dev.mysql.com/doc/refman/8.0/ja/sql-mode.html

このモードが有効な場合、GROUP BY句で指定されていないカラムをSELECT句に含めるとエラーが発生してしまいます。
今回は、そのエラーを解決するためのROW_NUMBERを使った解決策をご紹介します。

発生したエラーと解決したい課題

以下のようなクエリを実行しようとしたところ、エラーが発生しました。

SELECT
    department, name, salary
FROM
    employees
GROUP BY
    department
ORDER BY
    salary DESC;

このクエリは、部署ごとの給与上位者を取得しようとしていますが、ONLY_FULL_GROUP_BYモードが有効なため、GROUP
BY句で指定されていないnameカラムがSELECT句に含まれているのでエラーになってしまいます。

ROW_NUMBERを使った解決策

このエラーを解決するには、ROW_NUMBERとサブクエリを使用します。以下のように、クエリを修正してみましょう。

SELECT
    *
FROM (
    SELECT
        department, name, salary,
        ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num
    FROM
        employees
) AS subquery
WHERE
    row_num <= 3;

このクエリでは、サブクエリ内でROW_NUMBERを使用して、部署ごとに給与の降順で行番号を割り当てています。そして、サブクエリの結果に対して、行番号が3以下の行のみを取得しています。
実行結果は以下のようになります。

+------------+-----------+--------+---------+
| department | name      | salary | row_num |
+------------+-----------+--------+---------+
| 営業部     | ユーザーC | 570000 |       1 |
| 営業部     | ユーザーB | 550000 |       2 |
| 営業部     | ユーザーE | 520000 |       3 |
| 開発部     | ユーザーD | 650000 |       1 |
| 開発部     | ユーザーI | 620000 |       2 |
| 開発部     | ユーザーC | 600000 |       3 |
| 人事部     | ユーザーJ | 560000 |       1 |
| 人事部     | ユーザーG | 530000 |       2 |
+------------+-----------+--------+---------+

各部署の給与上位3名を取得できました!GROUP BY句を直接使用せずに、ROW_NUMBERとサブクエリを利用することで、ONLY_FULL_GROUP_BYモードでもエラーを回避できます。

より応用的な使い方

ROW_NUMBERを使ったテクニックは、他にも様々な場面で応用できます。例えば、以下のようなケースです。

  • 部署ごとの売上上位N名の営業担当者を取得する
  • カテゴリごとの売上上位N製品を取得する
  • 年度ごとの利益上位N店舗を取得する

これらの課題も、ROW_NUMBERとサブクエリを使用することで、簡単に解決できます。

注意点

ただし、ROW_NUMBERとサブクエリを使用する方法は、クエリの複雑さが増すため、パフォーマンスに影響を与える可能性があります。特に、大量のデータを扱う場合は、クエリの最適化が必要になる場合があります。
また、ROW_NUMBERは同順位の行に対して一意の行番号を割り当てますが、その順序は保証されません。同順位の行を一定の規則で並び替える必要がある場合は、追加の条件を指定する必要があります。

まとめ

以上、SQL_MODE=ONLY_FULL_GROUP_BYのエラーを解決するためのROW_NUMBERを使ったテクニックを紹介しました。このテクニックを使いこなせば、GROUP BY句で指定されていないカラムをSELECT句に含めたクエリも、エラーなく実行できるようになります。ぜひ、ご活用ください!

GitHubで編集を提案

Discussion