MySQLのデータ型の詳細な解説と選択ガイド
データ型は、データの保存方法、占有する容量、およびそのデータに対して実行可能な操作を決定します。不適切なデータ型を選択すると、深刻な結果を招く可能性があります。
このレッスンでは、MySQLのすべてのデータ型と、それらを選択するための戦略について、体系的に解説します。
graph TB
A[MySQL Data Types] --> B[Integer Types]
A --> C[Floating-point/Fixed-point type]
A --> D[String Type]
A --> E[Date and Time Type]
A --> F[JSON Type]
B --> B1[TINYINT]
B --> B2[SMALLINT]
B --> B3[INT]
B --> B4[BIGINT]
C --> C1[FLOAT]
C --> C2[DOUBLE]
C --> C3[DECIMAL]
D --> D1[CHAR]
D --> D2[VARCHAR]
D --> D3[TEXT]
D --> D4[ENUM]
E --> E1[DATE]
E --> E2[DATETIME]
E --> E3[TIMESTAMP]
1. 学習内容
- 整数型(TINYINT から BIGINT まで)
- 浮動小数点型および固定小数点型(FLOAT/DOUBLE/DECIMAL)
- 文字列型(CHAR/VARCHAR/TEXT/ENUM)
- 日付および時刻の型(DATE/DATETIME/TIMESTAMP)
- JSON 型 (MySQL 8.0 以降)
2. 精度に関する実話
(1) 課題:金額の計算に誤りがあった
あるECシステムでは、商品の価格はFLOAT型として保存されています:
SQL
-- Demonstration of Floating-Point Precision Loss
SELECT 0.1 + 0.2; -- Results: 0.300000004(approximate value,Inaccurate)
-- It becomes more apparent after it is entered into the table
CREATE TABLE float_demo (price FLOAT);
INSERT INTO float_demo VALUES (199.99);
SELECT price FROM float_demo; -- Results: 199.99(The display is correct, but there is a slight error in the internal storage.)
SELECT price + 0.01 FROM float_demo; -- Cumulative errors may become apparent during calculations
1,000件の取引を経て、累積した精度誤差により、財務照合で数元の不一致が生じた。
(2) DECIMAL の解法
SQL
-- DECIMAL Precise Storage,No error
CREATE TABLE decimal_demo (price DECIMAL(10,2));
INSERT INTO decimal_demo VALUES (199.99);
SELECT price + 0.01 FROM decimal_demo; -- Results: 200.00(Accurate)
結果:price = 200.00、誤りなく正確です。
| タイプ | 精度 | 適用シナリオ |
|---|---|---|
| FLOAT | おおよその値(6~7桁) | 科学計算;精度は不要 |
| ダブル | およその値 (15~16ビット) | 科学計算 |
| 10進数 | 有効桁数 | 金額、財務データ |
3. 整数型
| 型 | バイト数 | 符号付き範囲 | 符号なし範囲 |
|---|---|---|---|
| TINYINT | 1 | -128 ~ 127 | 0 ~ 255 |
| SMALLINT | 2 | -32,768 ~ 32,767 | 0 ~ 65,535 |
| MEDIUMINT | 3 | -8,388,608 ~ 8,388,607 | 0 ~ 16,777,215 |
| 入力 | 4 | -2,147,483,648 ~ 2,147,483,647 | 0 ~ 4,294,967,295 |
| BIGINT | 8 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 0 ~ 18,446,744,073,709,551,615 |
▶ 例:整数型
SQL
CREATE TABLE user_stats (
id INT PRIMARY KEY AUTO_INCREMENT,
age TINYINT UNSIGNED, -- Age 0-255 Enough
view_count INT UNSIGNED, -- Number of views
total_orders BIGINT UNSIGNED -- Total Number of Orders(It's very likely)
);
-- UNSIGNED Indicates unsigned(Only positive numbers)
INSERT INTO user_stats (age, view_count, total_orders)
VALUES (25, 1000000, 9999999999);
💡 ヒント: ストレージ容量を節約するために、要件を満たす最小のデータ型を選択してください。年齢には
TINYINT を、ユーザー ID には BIGINT を使用してください。
4. 浮動小数点型と固定小数点型
| 型 | バイト数 | 精度 | 用途 |
|---|---|---|---|
| FLOAT | 4 | 6~7桁 | 科学計算 |
| ダブル | 8 | 15~16ビット | 科学計算 |
| DECIMAL(M,D) | 変数 | 正確 | 金額、金融 |
M = 桁数、D = 小数点以下の桁数。
DECIMAL(10,2)は、最大 10 桁の整数 + 小数点以下 2 桁を表します。
▶ 例:DECIMAL の使用
SQL
CREATE TABLE financials (
id INT PRIMARY KEY,
amount DECIMAL(12,2), -- Amount,Maximum 9999999999.99
rate DECIMAL(5,4), -- Ratio,Maximum 9.9999
quantity DECIMAL(8,0) -- Number of integers
);
INSERT INTO financials VALUES (1, 1234567.89, 0.0850, 1000);
SELECT * FROM financials;
出力:
TEXT
+----+-------------+--------+----------+
| id | amount | rate | quantity |
+----+-------------+--------+----------+
| 1 | 1234567.89 | 0.0850 | 1000 |
+----+-------------+--------+----------+
5. 文字列型
| タイプ | 最大長 | 機能 | 用途 |
|---|---|---|---|
| CHAR(N) | 255 バイト | 固定長、高速 | 携帯電話番号、ID番号、MD5 |
| VARCHAR(N) | 65,535 バイト | 可変長、省スペース | ユーザー名、メールアドレス、役職 |
| TINYTEXT | 255 バイト | テキスト | 短いテキスト |
| テキスト | 65,535 バイト | テキスト | 記事本文 |
| MEDIUMTEXT | 16M | テキスト | 長文記事 |
| LONGTEXT | 4G | テキスト | 超長文 |
| ENUM | 65,535 個の値 | 列挙型 | ステータス、タイプ |
| セット | 64 個の値 | セット | 複数選択ラベル |
(1) CHAR 対 VARCHAR
SQL
-- CHAR(10) Fixed length, storing 'abc' uses 10 bytes
-- VARCHAR(10) Variable length, storing 'abc' uses 4 bytes (3 + 1 length prefix)
CREATE TABLE phones (
-- Mobile Number (Fixed 11 digits), use CHAR for faster lookup
mobile CHAR(11),
-- Username length varies, use VARCHAR to save space
username VARCHAR(50)
);
▶ 例:ENUM列挙型
SQL
CREATE TABLE orders (
id INT PRIMARY KEY,
status ENUM('pending', 'paid', 'shipped', 'completed', 'cancelled') DEFAULT 'pending'
);
INSERT INTO orders (id, status) VALUES (1, 'paid');
INSERT INTO orders (id) VALUES (2); -- Default 'pending'
-- ENUM stores numerical indexes (1,2,3...), not strings
SELECT status, status + 0 AS index_num FROM orders;
出力:
TEXT
+---------+-----------+
| status | index_num |
+---------+-----------+
| paid | 2 |
| pending | 1 |
+---------+-----------+
6. 日付および時刻の型
| タイプ | 形式 | 範囲 | バイト数 |
|---|---|---|---|
| 日付 | YYYY-MM-DD | 1000-01-01 ~ 9999-12-31 | 3 |
| 時間 | HH:MM:SS | -838:59:59 ~ 838:59:59 | 3 |
| 日時 | YYYY-MM-DD HH:MM:SS | 1000-01-01 ~ 9999-12-31 | 8 |
| タイムスタンプ | YYYY-MM-DD HH:MM:SS | 1970-01-01 ~ 2038-01-19 | 4 |
| 年 | YYYY | 1901 ~ 2155 | 1 |
(1) DATETIME と TIMESTAMP の違い
| ディメンション | DATETIME | TIMESTAMP |
|---|---|---|
| 範囲 | 1000–9999 | 1970–2038 |
| ストレージ | 8バイト | 4バイト |
| タイムゾーン | 変換しない | 自動的にUTCに変換 |
| 推奨事項 | 日付 | レコードの作成・変更日時 |
▶ 例:日付と時刻の使用
SQL
CREATE TABLE events (
id INT PRIMARY KEY AUTO_INCREMENT,
event_name VARCHAR(100),
event_date DATE,
start_time TIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
INSERT INTO events (event_name, event_date, start_time)
VALUES ('Tech Conference', '2026-09-15', '09:00:00');
SELECT * FROM events;
出力:
TEXT
+----+----------------+------------+------------+---------------------+---------------------+
| id | event_name | event_date | start_time | created_at | updated_at |
+----+----------------+------------+------------+---------------------+---------------------+
| 1 | Tech Conference| 2026-09-15 | 09:00:00 | 2026-07-03 10:00:00 | 2026-07-03 10:00:00 |
+----+----------------+------------+------------+---------------------+---------------------+
7. JSON データ型 (MySQL 8.0 以降)
▶ 例:JSONの扱い方
SQL
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
attributes JSON
);
INSERT INTO products (name, attributes) VALUES
('iPhone', '{"color": "black", "storage": 128, "tags": ["phone", "apple"]}');
-- Extract JSON value
SELECT
name,
attributes->>'$.color' AS color,
attributes->>'$.storage' AS storage
FROM products;
-- JSON Array Lookup
SELECT * FROM products WHERE JSON_CONTAINS(attributes->'$.tags', '"apple"');
出力:
TEXT
+--------+-------+---------+
| name | color | storage |
+--------+-------+---------+
| iPhone | black | 128 |
+--------+-------+---------+
8. データ型の選択に関するベストプラクティス
| シナリオ | 推奨タイプ | 理由 |
|---|---|---|
| 主キー ID | BIGINT AUTO_INCREMENT | 範囲が広く、パフォーマンスに優れる |
| ユーザー名 | VARCHAR(50) | 可変長、スペースを節約 |
| メールアドレス | VARCHAR(100) | 可変長 |
| 電話番号 | CHAR(11) | 固定長、高速検索 |
| パスワードハッシュ | CHAR(60) | bcryptは60文字に固定 |
| 金額 | DECIMAL(10,2) | 精密計算 |
| ステータス | ENUM または TINYINT | 有限の選択肢 |
| 記事本文 | テキスト | 詳細テキスト |
| 作成日時 | TIMESTAMP | 自動管理、4バイト |
| JSONデータ | JSON | インデックスと関数をサポート |
❓ よくある質問
Q VARCHAR(255) と VARCHAR(256) の違いは何ですか?
A VARCHAR の長さが 255 以下の場合、長さの保存に 1 バイトが使用されます。長さが 255 を超える場合は、2 バイトが使用されます。パフォーマンス上の根本的な違いはありませんが、255 は一般的に用いられる区切り点となっています。
Q TIMESTAMP 2038 問題についてはどうすればよいですか?
A MySQL 8.0 ではすでに修正が予定されています。懸念がある場合は、代わりに DATETIME を使用してください。
Q 1つのフィールドに複数の値を格納することはできますか?
A SETデータ型やJSON配列を使用することは可能ですが、関連するテーブルに分割することをお勧めします(これがより標準的な手法です)。
Q TEXT フィールドにインデックスを設定できますか?
A はい、ただし作成できるのはプレフィックス・インデックスのみです (
INDEX(col(100)))。フルインデックスが必要な場合は、VARCHAR の使用をご検討ください。📖 まとめ
- 整数型を範囲で選択:TINYINT → SMALLINT → INT → BIGINT
- 金額はDECIMAL型で保存する必要があります。FLOAT型やDOUBLE型は使用できません。
- CHAR:固定長(電話番号)、VARCHAR:可変長(ユーザー名)
- ENUM は、有限個の選択肢(状態、型)に適しています。
- TIMESTAMP は時刻の記録に適しており、DATETIME は業務上の日付の記録に適しています
- JSON 型は、柔軟な半構造化データをサポートしています
📝 練習問題
-
基本問題(難易度 ⭐):ECシステム用の
productsテーブルを設計し、商品名、価格、在庫数、ステータス、商品説明を格納するために適切なデータ型を選択してください。 -
上級演習(難易度 ⭐⭐):
user_profilesという名前のテーブルを作成し、その中に JSON 型のpreferencesというフィールドを含め、JSON 値の抽出とクエリの実践を行ってください。 -
課題(難易度:⭐⭐⭐):金額を保存する際、
DECIMAL(10,2)とFLOATの精度の違いを比較し、実験を通じてDECIMALの正確性を実証してください。



