エラーの内容
ERROR 1264 (22003): Out of range value for column 'age' at row 1
このエラーは、値がカラムのデータ型の範囲に収まらないときに発生します。MySQL のストリクトモード(5.7 以降はデフォルトで有効)は、不正なデータを黙って切り捨てることを拒否し、このエラーをスローします。
典型的な原因:age カラムが TINYINT(最大 127)なのに、300 という値が送られてきた場合です。もう一つよくあるケース:INT カウンターカラムに、2 つの大きな数値を掛け合わせた計算結果が入り、2,147,483,647 を超えてしまう場合です。
ステップ 1 — カラムのデータ型を確認する
問題のカラムの実際の型を確認します:
DESCRIBE users;
-- より詳しい情報:
SHOW COLUMNS FROM users LIKE 'age';
次のような出力が表示されます:
+-------+---------+------+-----+---------+
| Field | Type | Null | Key | Default |
+-------+---------+------+-----+---------+
| age | tinyint | YES | | NULL |
+-------+---------+------+-----+---------+
これで上限値がわかります。符号付き整数の範囲は以下のとおりです:
TINYINT : -128 to 127
SMALLINT : -32,768 to 32,767
MEDIUMINT : -8,388,608 to 8,388,607
INT : -2,147,483,648 to 2,147,483,647
BIGINT : -9.2 × 10^18 to 9.2 × 10^18
UNSIGNED を付けると正の上限が 2 倍になります。TINYINT UNSIGNED は 0〜255、SMALLINT UNSIGNED は 0〜65,535 になります。
ステップ 2 — 修正方法を選ぶ
3 つのアプローチがあります。どれが適切かは、カラムの型が狭すぎるのか、入力データが不正なのか、あるいはその両方なのかによって異なります。
修正 A — カラムの型を拡大する
正当な値が収まらない場合は、カラムを変更します:
-- age は TINYINT では 127 を超えられない — SMALLINT UNSIGNED に変更
ALTER TABLE users MODIFY COLUMN age SMALLINT UNSIGNED;
-- view カウンターが INT の上限 2.1B に達した場合 — BIGINT に切り替え
ALTER TABLE events MODIFY COLUMN view_count BIGINT UNSIGNED NOT NULL DEFAULT 0;
ALTER 後に失敗していた INSERT を再実行してください。今度は成功するはずです。
修正 B — 入力データを検証して修正する
不正なデータが送られてくる場合は、発生源を修正します。データベースレベルで検出するために MySQL の CHECK 制約を追加します:
-- MySQL 8.0.16+ では CHECK 制約をサポート
ALTER TABLE users ADD CONSTRAINT chk_age CHECK (age BETWEEN 0 AND 120);
-- 300 を挿入すると明確な制約エラーが発生する
INSERT INTO users (name, age) VALUES ('Alice', 300);
-- ERROR 3819 (HY000): Check constraint 'chk_age' is violated.
または、INSERT が実行される前にアプリ側で検証します:
# Python の例
if not (0 <= age <= 120):
raise ValueError(f"Age {age} is out of valid range")
修正 C — 負の値が不要な場合は UNSIGNED に切り替える
ID、カウント、サイズを格納するカラムは負の値を持つことがありません。それを明示することで、余裕を無料で 2 倍に増やせます:
ALTER TABLE orders MODIFY COLUMN quantity SMALLINT UNSIGNED NOT NULL DEFAULT 0;
-- SMALLINT UNSIGNED: 0 〜 65,535(符号付きの上限 32,767 との比較)
ストリクトモードについて
古い MySQL 5.6 サーバー、またはストリクトモードが無効になっているインスタンスでは、エラーを出さずに値を型の最大値・最小値にクランプしていました。現在のモードを確認します:
SELECT @@sql_mode;
出力に STRICT_TRANS_TABLES または STRICT_ALL_TABLES が表示されていれば、ストリクトモードが有効です。これは正しい動作です。このエラーは機能の一部であり、MySQL がデータを静かに破損することを拒否しているのです。
**回避策としてストリクトモードを無効にしないでください。**誤ったデータが黙って保存されるだけで、目に見えるエラーよりもはるかに深刻な問題を引き起こします。
MySQL が実際にどの値にクランプするか確認したい場合は、一時的にテストできます:
SET sql_mode = '';
INSERT INTO users (name, age) VALUES ('test', 300);
-- 127(TINYINT の最大値)が挿入される — エラーは出ないが、データは間違い
SELECT age FROM users WHERE name = 'test';
-- 127 が返される
テスト後はすぐにストリクトモードを再有効化してください:
SET sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
修正を確認する
ALTER 後に、型が実際に変更されたことを確認します:
SHOW COLUMNS FROM users LIKE 'age';
-- SMALLINT UNSIGNED と表示されるはず
次に、以前失敗していた INSERT を実行します:
INSERT INTO users (name, age) VALUES ('Bob', 300);
Query OK, 1 row affected (0.01 sec)
SELECT age FROM users WHERE name = 'Bob';
-- 300 が返される ✓
再発防止のためのヒント
- カラムは今日のデータだけでなく、実際のドメインに合わせてサイズを決める。
ageカラムに BIGINT は不要ですが、TINYINT は心もとない — SMALLINT UNSIGNED が適切な選択です。 - トラフィックの多いテーブルの ID と自動インクリメントカウンターには BIGINT を使う。 INT の 21 億という上限は、思ったよりも早く尽きます。
- ビジネスロジックの制限には CHECK 制約を追加する(MySQL 8.0.16+)。どのアプリが書き込んでも DB レベルでルールが強制され、エラーメッセージも明確です。
- **開発中に境界値をテストする。**リリース前に型の最小値と最大値を挿入しておく — エッジケースは本番より開発段階で発見する方がコストが低い。
- **ストリクトモードは有効のままにしておく。**データの暗黙的な切り捨ては、診断に何週間もかかるバグを生みます。最初からエラーが出る方が親切です。

