MySQLのTINYINT(1)には0と1以外も入ります!

概要

TINYINT(1)(1)の部分は表示幅(display width)の設定であって、格納できる値の制限ではありません。
これは公式ドキュメントにも記載があります。

Display width is unrelated to the range of values a type can store, as described in Section 13.1.6, “Numeric Type Attributes”.

The display width does not constrain the range of values that can be stored in the column.

そしてこの機能はMySQL 8.0.17時点で非推奨になった旨の記載もあります。

As of MySQL 8.0.17, the display width attribute is deprecated for integer data types; you should expect support for it to be removed in a future version of MySQL.

したがって、TINYINT(1)に格納できる値の範囲はTINYINTと同じで、-128から127までです。

実際に試してみる

DockerでMySQLのコンテナを起動して検証してみます。
.envファイルにはrootユーザーのパスワード(MYSQL_ROOT_PASSWORD)を記載しておきます。

$ docker run --env-file .env --name mysql_test -d mysql:8.0.41

コンテナの中に入ります。

$ docker exec -it mysql_test bash

コンテナに入るとmysqlコマンドが使えるので、MySQLに接続します。

bash-5.1# mysql -u root -p

適当なデータベースと検証用のテーブルを作成します。

mysql> CREATE DATABASE example;
Query OK, 1 row affected (0.00 sec)

mysql> USE example;
Database changed
mysql> CREATE TABLE example (id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,v1 TINYINT(1),v2 TINYINT);
Query OK, 0 rows affected, 1 warning (0.02 sec)

適当な値を追加します。

mysql> INSERT INTO example (v1,v2) VALUES (100,100);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO example (v1,v2) VALUES (-100,-100);
Query OK, 1 row affected (0.01 sec)

追加した値を表示してみると、TINYINT(1)を指定したv1にも100や-100といった値が格納されていることがわかります。

mysql> SELECT * FROM example;
+----+------+------+
| id | v1   | v2   |
+----+------+------+
|  1 |  100 |  100 |
|  2 | -100 | -100 |
+----+------+------+
2 rows in set (0.00 sec)

考察

“mysql tinyint(1)"とかでGoogle検索してみると、TINYINT(1)は1 bitの値だから0 (false)か1 (true)しか入らない、といった説明を比較的多く見かけます。
もしかしてMySQLの古いバージョンだとそういう動作だったのか、と思ったのですが、2012年のStack Overflowの質問でも(M)は表示幅だから値の範囲とは関係ない、という回答があるので、そういうわけでもなさそうです。

MySQLのBOOLEANと一般的なプログラミング言語のブール値が混同された結果なのかな、とも思います。
MySQLのBOOLEANTINYINT(1)として扱われるため、ブール値にはfalseかtrueしか入らない、つまりTINYINT(1)は0 (false)か1 (true)しか入らない型である、という推測です。

まあ、実際のところどういう経緯で広まった説明なのかはわかりませんが…。

MySQL,技術

Posted by 駄場さん