できない.dev

MySQL で only_full_group_by のエラーが出て GROUP BY が通らない

GROUP BY に無い列を選ぶと、どの行の値を返すか一意に決まらないため MySQL は ERROR 1055 で拒否する。
集約関数で包むか GROUP BY に足すか、値を問わないと宣言する ANY_VALUE() を使う。

公開: 更新:

実行例あり(2026-09-25 に実環境で検証)

要約

このエラーは、SELECT 句の列が GROUP BY によって一意に決まらないときに出る。
グループが複数行を含むのに、その中のどの行の値を返せばよいか SQL として決まらないため、MySQL は推測せずに拒否する。

SELECT name, address, MAX(age) FROM t GROUP BY name;
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause
and contains nonaggregated column 'mydb.t.address' which is not functionally
dependent on columns in GROUP BY clause; this is incompatible with
sql_mode=only_full_group_by

only_full_group_by は Server SQL Modes(新しいタブで開く) が示すとおり MySQL 8.4 の既定の sql_mode に含まれる。
古い設定から移ってきたクエリが、ここで初めて弾かれることが多い。

実行例

公式イメージ mysql:8.4 の既定の sql_mode(先頭が ONLY_FULL_GROUP_BY)では、GROUP BY name に無い address を選ぶと ERROR 1055、GROUP BY なしで name と MAX(age) を並べると ERROR 1140 で拒否された。address を MAX() か ANY_VALUE() で包むと通り、主キー u.id での GROUP BY なら u.name を GROUP BY に足さなくても結果が返っている。

$ mysql -u root --force -v mydb < bad.sql
--------------
SELECT name, address, MAX(age) FROM t GROUP BY name
--------------
 
ERROR 1055 (42000) at line 1: Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'mydb.t.address' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
--------------
SELECT name, MAX(age) FROM t
--------------
 
ERROR 1140 (42000) at line 2: In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'mydb.t.name'; this is incompatible with sql_mode=only_full_group_by
$ mysql -u root -t mydb < sql_mode.sql
+-----------------------------------------------------------------------------------------------------------------------+
| @@SESSION.sql_mode                                                                                                    |
+-----------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+-----------------------------------------------------------------------------------------------------------------------+
$ mysql -u root -t -v mydb < solve_aggregate.sql
--------------
SELECT name, MAX(address) AS address, MAX(age) AS age
FROM t
GROUP BY name
--------------
 
+-------+---------+------+
| name  | address | age  |
+-------+---------+------+
| alice | tokyo   |   35 |
| bob   | nagoya  |   40 |
+-------+---------+------+
--------------
SELECT name, ANY_VALUE(address) AS address, MAX(age) AS age
FROM t
GROUP BY name
--------------
 
+-------+---------+------+
| name  | address | age  |
+-------+---------+------+
| alice | tokyo   |   35 |
| bob   | nagoya  |   40 |
+-------+---------+------+
$ mysql -u root -t -v mydb < solve_pk.sql
--------------
SELECT u.id, u.name, COUNT(o.id) AS orders
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id
--------------
 
+----+-------+--------+
| id | name  | orders |
+----+-------+--------+
|  1 | alice |      2 |
|  2 | bob   |      1 |
+----+-------+--------+

— 2026-09-25 時点の出力

検証環境

検証日
実行環境
mysql:8.4 Oracle Linux Server 9.8

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. 非集約列の混入: GROUP BY name に対して address を選ぶと、同じ name の中で address が複数あり得る。
    値が 1 つに定まらないので拒否される。
  2. HAVING / ORDER BY での参照: 制約は SELECT 句だけではない。MySQL Handling of GROUP BY(新しいタブで開く) は HAVING と ORDER BY も同じ条件で検査すると説明している。
  3. GROUP BY を書いていない集約: SELECT name, MAX(age) FROM t; のように GROUP BY 無しで集約関数を使うと、全体が 1 グループになり name が定まらない。
    この場合は ERROR 1140 になる。
  4. 環境差: 開発環境で sql_mode から外していると通り、本番の既定設定で落ちる。
    逆にマネージドサービスへ移行して急に出ることもある。
  5. ORM の生成クエリ: 集計用のスコープに、一覧表示用の列選択が残っていると混入する。
    生成された SQL をログで確認しないと気づきにくい。

解決策

1. 集約関数で包む

SELECT name, MAX(address) AS address, MAX(age) AS age
FROM t
GROUP BY name;

「代表値を 1 つ選ぶ」という意図が SQL 上に現れるため、後から読む人にも判断が伝わる。
ただし集約は列ごとに別々に行われるので、MAX(address) と MAX(age) が同じ行の値の組になるとは限らない。age が最大の行の address が欲しいなら、ROW_NUMBER() OVER (PARTITION BY name ORDER BY age DESC) で各グループの行を 1 つに絞ってから選ぶ。

2. GROUP BY に追加する

SELECT name, address, MAX(age) FROM t GROUP BY name, address;

ただしグループの粒度が変わる。name ごとの集計が欲しかったのなら結果の意味が変わってしまうので、意図と合うか確認する。

3. 主キーでグループ化する

SELECT u.id, u.name, COUNT(o.id) AS orders
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id;

u.id が主キーなら u.name はそこから一意に決まる。
MySQL はこの関数従属を認識するので、u.name を GROUP BY に足さなくても通る。

4. ANY_VALUE() で明示する

SELECT name, ANY_VALUE(address) AS address, MAX(age) AS age
FROM t
GROUP BY name;

ANY_VALUE() は「どの行の値でも構わない」と宣言する関数で、その列についてだけ検査を抑止する。sql_mode を丸ごと変えるより影響範囲が小さい。MAX(age) と並べても、その最大値を持つ行の address が返るとは限らない点は解決策 1 と同じである。

5. 設定を確認する

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

環境ごとに値が違うなら、まずそこを揃える。only_full_group_by を外す対処は、これまで気づかずに済んでいた不定なクエリをそのまま通してしまうので、最後の手段にする。

この記事は役立ちましたか?