サイトアイコンmaita tomoya dev io

#83 MySQL の tinyint(1) と tinyint は何が違う?— 整数型 display width 非推奨の話

はじめに

MySQL のテーブル定義で tinyint(1) や int(11) のように、整数型にカッコ付きの数値を見たことがあるだろうか。この (N) は display width(表示幅) と呼ばれるもので、MySQL 8.0.17 以降で非推奨になっている。

本記事では、display width とは何か、なぜ非推奨になったのか、新規テーブル設計ではどうすべきかを整理する。


display width とは

基本

整数型の (N) は 表示幅のヒント であり、格納できる値の範囲には一切影響しない。

tinyint       -- display width 指定なし
tinyint(1)    -- display width = 1
tinyint(3)    -- display width = 3

上記はすべて同じ tinyint 型(1バイト、-128〜127)であり、格納サイズも値の範囲もまったく同じである。

ZEROFILL との組み合わせ

display width が効果を持つのは ZEROFILL 属性と組み合わせた場合 のみ である。

-- ZEROFILL ありの場合
val tinyint(3) ZEROFILL

-- 値が 5 の場合 → 005 と表示される
-- 値が 42 の場合 → 042 と表示される

ZEROFILL を使っていなければ、(N) は完全に無意味な指定となる。


tinyint(1) と tinyint の違い

結論から言うと、動作上の違いはない。

項目tinyinttinyint(1)
格納サイズ1バイト1バイト
値の範囲-128〜127-128〜127
UNSIGNED の場合0〜2550〜255
ZEROFILL なしでの違いなしなし

なぜ tinyint(1) をよく見るのか

MySQL は BOOLEAN 型を tinyint(1) のエイリアス として扱う。BOOLEAN や BOOL でカラムを定義すると、内部的に tinyint(1) になる。

-- この2つは同じ意味
status BOOLEAN DEFAULT FALSE
status tinyint(1) DEFAULT 0

このため「0/1 のフラグには tinyint(1)」という慣習が広まった。しかし (1) があっても 0 と 1 以外の値(例えば 127)も普通に格納できてしまう。値の制約にはなっていない。


なぜ非推奨になったのか

MySQL 8.0.17 のリリースノートで以下のように記載されている。

The display width attribute for integer data types is deprecated and will be removed in a future MySQL version.

参考:MySQL 8.0.17 Release Notes

非推奨の理由は主に3つ。

1. 誤解を招く

多くの開発者が (N) を「格納できる桁数」や「値の範囲の制限」だと誤解している。

tinyint(1)  -- 「0〜9 の1桁だけ入る」→ 誤解。-128〜127 が入る
int(3)      -- 「0〜999 だけ入る」→ 誤解。約 -21億〜21億 が入る

2. ZEROFILL なしでは意味がない

display width が効果を持つのは ZEROFILL との組み合わせだけだが、ほとんどのテーブルで ZEROFILL は使われていない。つまり大半のケースで (N) は無意味な記述である。

3. ZEROFILL 自体も不要

ゼロ埋めはアプリケーション側で行うべき表示ロジックであり、データ型の責務ではない。MySQL は LPAD() 関数などで代替できる。

-- ZEROFILL の代わりに
SELECT LPAD(val, 3, '0') FROM example;
-- 5 → '005'

まとめると「ほぼ誰も正しく使っていない機能を残しておくと害の方が大きい」という判断で非推奨となった。


新規テーブル設計での推奨

display width を付けない

新しくテーブルを作る場合は、display width なしで定義する。

-- 推奨
status tinyint NOT NULL DEFAULT 0

-- 非推奨(動作はするが無意味)
status tinyint(1) NOT NULL DEFAULT 0

NOT NULL を意識する

ON/OFF のフラグカラムで DEFAULT だけ設定して NOT NULL を付け忘れると、明示的に NULL を INSERT できてしまう。

-- NULL が入り得る(意図しない可能性が高い)
status tinyint DEFAULT 0

-- NULL を許容しない(安全)
status tinyint NOT NULL DEFAULT 0

既存テーブルとの統一性

既存テーブルが tinyint(1) で統一されているスキーマでは、無理に変える必要はない。ただし、DB 全体で tinyint(display width なし)の方が多い場合は、新規テーブルでは tinyint を使う方が自然である。


まとめ

項目内容
tinyint(1) の (1)display width(表示幅のヒント)
値の範囲への影響なし
効果があるケースZEROFILL との組み合わせのみ
非推奨MySQL 8.0.17 以降
非推奨の理由誤解を招く、ZEROFILL なしでは無意味
新規テーブルでの推奨tinyint(display width なし)を使う

レガシーなスキーマでは tinyint(1) が大量に残っているが、新規テーブルでは display width を付けないシンプルな定義にしていくのがよいだろう。


参考