Loading

ROUTE

ルートゼロの
アクティビティ

MySQLのONLY_FULL_GROUP_BYエラーを直す

1

MySQLで「Expression #… is not in GROUP BY clause」「ONLY_FULL_GROUP_BY」と表示されたら、SELECTの非集計列がGROUP BYに含まれず、MySQLがグループ内の値を一意に決められない状態を疑います。まず必要な集計単位を確定し、不要な列はSELECTから外す、グループ化する列を追加する、集計関数を使う、の順に直しましょう。sql_modeを無条件に無効化する方法は勧めません。

ONLY_FULL_GROUP_BYエラー1055の原因を確認する

MySQLのONLY_FULL_GROUP_BYは、GROUP BYで集計するときに、SELECTやHAVING、ORDER BYで参照する非集計列を制限するsql_modeです。GROUP BY列に含まれない非集計列でも、MySQLが機能従属性を認識できれば許可される場合があります。

SQLの状態 理由 対応の方向
非集計列がGROUP BYにない グループ内で値を一つに決められない SELECT列または集計単位を修正
グループ化された列から値を一意に決められる 機能従属性が認識されれば有効な場合がある ユニーク制約・主キーを確認
HAVINGに非集計列を使っている GROUP BYとの整合性が必要 集計式・列の依存関係を確認

用語解説:機能従属性(functional dependence)
ある列の値が決まると別の列の値も一意に決まる関係です。MySQLは主キーや一意でNOT NULLの列などから認識できる場合があります。


エラーになるSQLを小さな例で再現する

ordersテーブルに同じcustomer_idの注文が複数ある例を考えます。以下のSQLは顧客ID別の合計を求めますが、GROUP BYにないcustomer_nameをそのままSELECTしています。customer_idにはUNIQUE制約がない想定です。

SELECT
    customer_id,
    customer_name,
    SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;

同じcustomer_idに異なるcustomer_nameが登録されていたら、どの名前を返すか決まりません。このためMySQLのONLY_FULL_GROUP_BYモードでは通常エラー1055になります。ここで注文金額を単純に集計したいだけなのか、表示用の顧客名も必要なのかを分けて考えます。


修正1:集計に不要な列をSELECTから外す

顧客IDごとの合計のみが必要な場合はcustomer_nameを外すのが簡潔です。意味のない列までGROUP BYへ追加する必要はありません。

SELECT
    customer_id,
    SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;

このSQLはcustomer_idごとに注文額を合計します。もとのエラーを回避するために集計単位を増やしていないので、顧客名の表記ゆれがあっても合計行の数が増えません。


修正2:GROUP BYへ列を追加するときの注意

顧客IDと名前の組み合わせで集計するなら両方をGROUP BYへ指定できます。ただし顧客名が変わるとグループも分かれるので、顧客IDだけの合計とは意味が異なります。

SELECT
    customer_id,
    customer_name,
    SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id, customer_name;
customer_id customer_name amountの例
10 佐藤 1000
10 佐藤 3000
10 佐藤商事 2000

上記のように同じIDに複数の名前が混在するデータでは、列を足すだけで別グループになる点に注意してください。顧客名を正規のマスターテーブルから取得するなど、データ設計側の改善も検討します。

修正3:集計関数とANY_VALUEの使い分け

値が複数ある場合に「最大値が欲しい」ならMAXなどの集計関数を明示できます。ただしMAX(customer_name)は文字列の大小に基づいて一つを選ぶだけで、正しい顧客名を決める仕組みではありません。

SELECT
    customer_id,
    MAX(customer_name) AS max_name,
    SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;

MySQLにはANY_VALUEもあり、非集計列をGROUP BY制約の対象から外す用途に使えます。ただし同じグループに異なる値があると、どの値が返るかを保証しません。値が同一と分かっている場合など、意味を確認して限定的に使います。

SELECT
    customer_id,
    ANY_VALUE(customer_name) AS sample_name,
    SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;

ANY_VALUEを付ければ品質上の問題が解決するわけではありません。表示すべき名前が一意に決まるなら、主キーを持つ顧客マスターへのJOIN等で関係を明確にする方針を検討してください。

sql_modeの確認と無効化を避ける理由

SELECT @@SESSION.sql_mode;
SELECT @@GLOBAL.sql_mode;

ONLY_FULL_GROUP_BYの有無は、セッションやサーバー全体のSQLモードを表示して確認できます。環境差が原因の場合は特に有用です。

sql_modeからONLY_FULL_GROUP_BYを削除するとエラー自体を回避できる場合がありますが、グループ内の非集計列が不定の値を返す危険もあります。影響を確認しないまま本番設定を変更せず、まずSQLの集計意図を修正してください。

  • SELECT句の非集計列を洗い出す。
  • 集計単位を定義し直す。
  • 主キー・ユニーク制約による機能従属性を確認する。
  • 複数値が存在する場合、1件を選んでよい設計なのかを確認する。

JOINやORDER BYを足しても直らない場合

JOINによって複数行に増えた場合、集計前にどの単位でデータが増えているか確認する必要があります。ORDER BYを追加しても、GROUP BY中にどの非集計列の値を選ぶかを制御できません。

GROUP BYを使う理由、JOINの多重度、WHERE/HAVINGの位置を順番に切り分けると、エラーメッセージだけでなく集計結果の誤りも減らせます。

(GROUP BYとWHERE・HAVINGの基本については『GROUP BY使い方完全解説|SQLエラー・WHERE HAVINGの違いは?』をご参照ください)

(実行計画と集計SQLの見直しについては『SQLチューニングとは?遅いSQLを最短で改善する3つの手順』をご参照ください)

よくある疑問とまとめ

ONLY_FULL_GROUP_BYはMySQLの不具合?

不具合ではなく、曖昧な集計結果を抑えるためのSQLモードです。列の指定と集計の意味が整合するよう修正します。

主キーでGROUP BYすれば他の列もSELECTできますか?

MySQLが機能従属性を認識できる場合は許可されます。ただし実際の一意制約やNULL可否などの条件を確認してください。

ANY_VALUEを使えば常に正しい?

いいえ。ANY_VALUEは一意でない列から意味上正しい値を選ぶ保証はありません。集計後にどの値を表示すべきかを先に決めましょう。

MySQLのONLY_FULL_GROUP_BYエラーは、集計単位と非集計列の指定が一致していないサインです。不要な列を外す、GROUP BYを増やす場合は集計粒度が変わることを確認する、明確な集計関数を使う、という順で対応してください。

まずはエラーになったSELECT文の列を一つずつチェックし、求めたい集計単位を確認してみてください。

カジュアル面談はこちら

RANKINGranking-icon

すべてを開示している会社です

ポジティブな面もネガティブな面も、包み隠さず誠実にお答えします。
気になることがあれば、面談の際にすべてオープンにお話します。