MySQL5.7のExtended Supportは2023年10月までです。これから始める場合はMySQL8.0を使いましょう。
既存のシステムのMySQLを5.6から5.7に変えて動かしたら、group by
を使ったSQLでエラーが発生した。
Error Number: 1055
Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column
'dtabase_name.table_name.column_name' which is not functionally dependent on columns in GROUP BY clause;
this is incompatible with sql_mode=only_full_group_by
このエラーは sql_mode
システム変数値に起因するもので、 ONLY_FULL_GROUP_BY
が設定されている場合に発生する。
このモードの詳しい説明は 5.1.7 サーバー SQL モード に詳しく記載されている。5.6のマニュアルであるが日本語なので理解しやすい。
例えば GROUP BY 句
に指定がないカラムを SELECT 句
の選択リストに指定するとエラーとなる。
SELECT name, address, MAX(age) FROM t GROUP BY name;
この例の場合は address
カラムが GROUP BY 句
に指定がないのでエラーとなる。
sql_mode
のデフォルト値
- 5.6 >= 5.6.6:
NO_ENGINE_SUBSTITUTION
- 5.7:
ONLY_FULL_GROUP_BY
,STRICT_TRANS_TABLES
,NO_ZERO_IN_DATE
,NO_ZERO_DATE
,ERROR_FOR_DIVISION_BY_ZERO
,NO_AUTO_CREATE_USER
,NO_ENGINE_SUBSTITUTION
5.7から sql_mode
のデフォルト値が変更されたので今回のようなエラーが発生してしまう。
SQLを修正すれば良いが難しいケースもあるので、その場合は sql_mode
を変更しよう。
sql_mode
の確認方法
MySQLコンソールで以下のように show variables
を実行すれば確認できる。
mysql> show variables like 'sql_mode';
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| sql_mode | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
しかし、ここで表示されるのはSession変数としてのsql_mode
なので、Global変数を確認するには別の方法で確認が必要となる。
-- グローバル変数を確認
mysql> select @@global.sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@global.sql_mode |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
-- セッション変数を確認
mysql> select @@session.sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@session.sql_mode |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
sql_mode
のコマンドによる変更
sql_mode
は set
ステートメントで変更することができる。グローバルに変更する場合は global
を指定する。
mysql> set global sql_mode='NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> select @@GLOBAL.sql_mode;
+------------------------+
| @@GLOBAL.sql_mode |
+------------------------+
| NO_ENGINE_SUBSTITUTION |
+------------------------+
mysql> select @@session.sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@session.sql_mode |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
しかし、グローバル変数を変えただけではセッション変数には反映されない。反映させるには切断して再接続する必要がある。
セッション変数を変える場合は session
を指定する。(もしくは何も指定しない)
mysql> set session sql_mode = 'NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected (0.00 sec)
-- または
mysql> set sql_mode = 'NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected (0.00 sec)
mysql> select @@session.sql_mode;
+------------------------+
| @@session.sql_mode |
+------------------------+
| NO_ENGINE_SUBSTITUTION |
+------------------------+
1 row in set (0.00 sec)
sql_mode
の起動パラメーターによる変更
サーバー起動時に --sql-mode=""
オプションを使用することで sql_mode
を設定できる。
mysqld --sql-mode="NO_ENGINE_SUBSTITUTION"
sql_mode
のmy.cnf
による変更
my.cnf
に sql-mode=""
を指定することで sql_mode
を設定できる。
[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
DockerでMySQLコンテナを起動する時に sql_mode
を設定するには
起動パラメーターで設定する
docker run
コマンドでコンテナを起動する場合は、起動コマンドとして --sql-mode=""
を与えることで指定が可能となる。
$ docker run --rm -e MYSQL_ROOT_PASSWORD=root mysql:5.7 --sql-mode=NO_ENGINE_SUBSTITUTION
DockerComposeの場合も同様に起動コマンドに与えることができる。以下のように docker-compose.yml
の command:
ディレクティブに設定する。
version: '3.7'
services:
db:
image: mysql:5.7
command:
- --sql-mode=NO_ENGINE_SUBSTITUTION
environment:
MYSQL_ROOT_PASSWORD: root
my.cnf で設定する
docker run
コマンドで my.cnf
をコンテナにわたすにはVolumeとしてマウントする。
docker run --rm -e MYSQL_ROOT_PASSWORD=root -v /path/to/my.cnf:/etc/mysql/conf.d/my.cnf mysql:5.7
MySQL5.7のコンテナでは /etc/mysql/conf.d/
ディレクトリに任意のファイルを配置することで起動時にロードしてくれる。
また、 MySQLでは複数の my.cnf
を順にロードする仕組みがあるのでその中でも順番が遅いファイルにマウントするほうが安全だろう。
my.cnf
の読み込み順を確認するには mysql --help | grep 'my.cnf
を実行すると確認できる
$ mysql --help | grep my.cnf
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
この場合以下の順でロードされるが、/etc/mysql/my.cnf
の中で /etc/mysql/conf.d/
をincludeしているので /etc/mysql/conf.d/my.cnf
にマウントしてもロードされる。
/etc/my.cnf
/etc/mysql/my.cnf
~/.my.cnf
DockerComposeの場合は docker-compose.yml
の volumes:
ディレクティブに設定できる。
version: '3.7'
services:
db:
image: mysql:5.7
volumes:
- /path/to/my.cnf:/etc/mysql/conf.d/my.cnf
environment:
MYSQL_ROOT_PASSWORD: root