MySQL のデータ型
MySQLでテーブルを作成する際、各カラム(列)に正しいデータ型(Data Type)を定義することは非常に重要です。データ型は、そのカラムがどのようなデータ(数値、テキスト、日付など)を格納できるか、どの程度のストレージ容量を占有するか、そしてMySQLがそのデータに対してどのような処理を行うかを決定します。適切なデータ型を選択することは、データの整合性を保証するだけでなく、ストレージスペースの最適化やクエリパフォーマンスの向上にも直結します。
1. 数値データ型
数値データ型は数値を格納するために使用されます。MySQLは、異なる範囲や精度のニーズに応えるために、主に「整数型」と「定点数型/浮点数型」の2つのカテゴリで多様な数値型を提供しています。
1.1 整数型
整数型は、小数点以下のない完全な数字(正数または負数)を格納するために使用されます。各整数型には特定の格納範囲があります。テーブル設計における一般的な最適化戦略は、「予想される数値範囲をカバーできる最小のデータ型を使用すること」です。
- TINYINT:非常に小さな整数を格納します。
- 有符号(Signed)範囲:-128 ~ 127
- 無符号(Unsigned)範囲:0 ~ 255
- 例:製品レビューの星評価(1~5星)など。
- SMALLINT:小さな整数を格納します。
- 有符号範囲:-32,768 ~ 32,767
- 無符号範囲:0 ~ 65,535
- 例:小規模な注文における商品数量など。
- MEDIUMINT:中程度の大きさの整数を格納します。
- 有符号範囲:-8,388,608 ~ 8,388,607
- 無符号範囲:0 ~ 16,777,215
- 例:小都市の人口数など。
- INT (または INTEGER):最も一般的に使用される整数型。
- 有符号範囲:-2,147,483,648 ~ 2,147,483,647
- 無符号範囲:0 ~ 4,294,967,295
- 例:顧客や製品のID。BIGINTを必要としない一般的な整数シナリオに適しています。
- BIGINT:極めて大きな整数を格納します。
- 有符号範囲:-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
- 無符号範囲:0 ~ 18,446,744,073,709,551,615
- 例:高負荷な金融システムにおける一意のトランザクションIDや、人気動画の膨大な再生回数など。
ヒント:整数型を定義する際、オプションとして UNSIGNED(無符号)キーワードを追加できます。これにより負数を禁止し、正数の格納範囲を拡大できます。例えば、INT UNSIGNED は非負の整数(0 ~ 4,294,967,295)のみを格納することを意味し、有符号INTの正数範囲を実質2倍に拡大します。
1.2 定点数型
定点数型は正確な数値を格納するために使用され、精度が極めて重要で丸め誤差が許されない財務データの管理に最適です。
- DECIMAL(M, D) (または NUMERIC(M, D)):正確な数値を格納します。Mは総桁数(精度)、Dは小数点以下の桁数(スケール)を表します。
- Mの最大値は65、Dの最大値は30です。
- 例:
DECIMAL(10, 2)は最大 99999999.99 までの数字を格納できます(小数点前8桁、小数点後2桁)。これは、19.99ドルのような通貨金額や、精密な科学的測定値の表現に適しています。 - 想定シナリオ:暗号資産取引所が高精度で取引金額を格納する必要がある場合。
DECIMAL(20, 8)を使用することで、0.00000001 BTC のような数値を正確に保存できます。 - 実際のケース:ECサイトのデータベースで商品価格を格納する場合。価格は正確である必要があり、
DECIMAL(7, 2)で 99999.99 までの価格を管理します。また、消費税の計算など、税率や最終金額に極めて高い精度が求められる場合にも使用されます。
1.3 浮点数型
浮点数型は近似値を格納するために使用されます。これらは絶対的に正確ではなく、精度の損失が発生する可能性があるため、通常は財務計算には適していません。しかし、近似値が許容される科学データや工学データには非常に実用的です。
- FLOAT(M, D):単精度浮動小数点数。Mは総桁数、Dは小数点以下の桁数。具体的な範囲と精度はハードウェアに依存します。
- 例:
FLOAT(7, 4)は123.4567のような値を格納できます。精度要求が高くなく、一定の誤差が許容される測定データに適しています。 - DOUBLE(M, D) (または REAL):倍精度浮動小数点数。FLOATよりも高い精度を提供します。
- 例:
DOUBLE(10, 6)は1234.567890のような値を格納できます。 - 実際のケース:GPS座標(経緯度)の保存。これらのデータは通常高い精度が必要ですが、浮動小数点の近似的な性質を許容できます。例えば、地図アプリで経緯度を保存するために
DOUBLEを使用します。また、センサーの読み取り値や物理的な測定値など、測定自体に固有の誤差が浮動小数点の精度問題を上回るような科学データの保存にも利用されます。
2. 文字列データ型
文字列データ型はテキストデータを格納するために使用されます。MySQLは、異なるストレージ特性と最大長を持つ多様なオプションを提供しています。
2.1 固定長と可変長
- CHAR(L):固定長文字列。Lは格納する正確な文字数を表します(範囲:0 ~ 255)。
- 入力された文字列がLより短い場合、システムは指定された長さまでスペースでパディング(埋め合わせ)します。読み取り時、
PAD_CHAR_TO_FULL_LENGTHSQLモードが有効でない限り、末尾のスペースは自動的に削除されます。 - 例:
CHAR(3)に 'abc' を保存すると3文字消費します。'ab' を保存しても3文字消費します('ab ' としてパディング)。 - いつ使うか:カラム内のすべての値の長さが完全に一致する場合に使用します。例えば、2文字の都道府県/州の略称(
CHAR(2))や、固定長の商品コードなど。 - 実際のケース:国コード('US', 'JP', 'CN' など)の保存。これらは常に2文字です。
- VARCHAR(L):可変長文字列。Lは格納可能な最大文字数を表します(範囲:0 ~ 65,535)。
- 実際に入力された文字数のみを保存し、さらに文字列の長さを記録するために1~2バイトを消費します。
- 例:
VARCHAR(255)に 'Hello' を保存すると、5文字分に加えて長さ記録用のバイトを消費します。 - いつ使うか:氏名、住所、製品説明など、長さの変化が大きい一般的な文字列の保存に使用します。
- 実際のケース:顧客の名前(
VARCHAR(50))やメールアドレス(VARCHAR(255))。これらの値は長さが異なるため、VARCHARを使用することでスペースを節約できます。
2.2 テキスト型 (TEXT)
- TINYTEXT:最大長255文字のテキストカラム。
- 例:短いメモやコメントなど。
- TEXT:最大長65,535文字のテキストカラム。
- 例:記事の本文や詳細な製品説明など、長いテキストに適しています。
- MEDIUMTEXT:最大長16,777,215文字のテキストカラム。
- 例:ブログ記事全体や書籍の一つの章などの保存。
- LONGTEXT:最大長4,294,967,295文字のテキストカラム。
- 例:書籍一冊の内容や膨大なログファイルなど、極めて巨大なテキストデータ。
2.3 特殊な型
- ENUM:列挙型。テーブル作成時に指定した許可リストの中から、一つの値を選択する必要があります。
- 例:製品サイズに
ENUM('Small', 'Medium', 'Large')を使用。 - メリット:内部的には文字列そのものではなく整数インデックスとして格納されるため、非常に効率的です。
- 実際のケース:注文ステータス('Pending'(保留中), 'Processing'(処理中), 'Shipped'(出荷済), 'Delivered'(配達済))。
- SET:集合型。作成時に指定したリストから、0個以上の値を含めることができます。
- 例:製品に関連する色に
SET('Red', 'Green', 'Blue')を使用。一つの製品が 'Red', 'Green' であったり、'Red, Blue' を同時に含んだりすることが可能です。 - 内部メカニズム:ビットマスク(Bitmask)形式で複数のオプションを格納します。
- 想定シナリオ:ユーザーの通知受信設定。例えば
SET('Email', 'SMS', 'Push Notification')。ユーザーは 'Email, SMS' を同時に選択できます。
3. 日付と時間データ型
日付と時間データ型は、時間軸の値を格納するために使用されます。MySQLは、日付、時間、またはその両方の組み合わせを格納するための専用の型を提供しています。
3.1 日付と時間の基本
- DATE:'YYYY-MM-DD' 形式で日付を格納します。
- 範囲:'1000-01-01' ~ '9999-12-31'
- 例:個人の生年月日や製品の発売日など。
- TIME:'HH:MM:SS' 形式で時間を格納します。
- 範囲:'-838:59:59' ~ '838:59:59'。この広い範囲は、1日の中の特定の時刻だけでなく、経過時間や時間間隔も格納できることを意味します。
- 例:映画の上映時間や会議の開始時間。
- DATETIME:'YYYY-MM-DD HH:MM:SS' 形式で日付と時間を組み合わせて格納します。
- 範囲:'1000-01-01 00:00:00' ~ '9999-12-31 23:59:59'
- 例:注文生成の正確なタイムスタンプや、特定のイベントが発生した瞬間。
- 実際のケース:ユーザーアカウントの作成日時(例:2023-10-26 14:30:00)。
3.2 タイムスタンプと年
- TIMESTAMP:DATETIMEと同様に日付と時間の組み合わせを格納しますが、範囲が狭く、主にレコードの変更を追跡するために使用されます。Unixエポック(1970年1月1日 UTC)からの秒数を格納します。
- 範囲:'1970-01-01 00:00:01' UTC ~ '2038-01-19 03:14:07' UTC
- 特性:
UPDATE操作時に明示的に値を設定しない場合、自動的に現在の時刻に更新されるように設定できます。これはlast_updated(最終更新日時)カラムに最適です。 - 保存時に現在のタイムゾーンの時間をUTCに変換し、読み取り時にUTCから現在のタイムゾーンの時間に再変換します。
- 例:レコードが最後に修正された時間を追跡。
- 実際のケース:
productsテーブル内のlast_modifiedカラム。製品詳細が変更されるたびに、このカラムが現在のタイムスタンプに自動更新されます。 - YEAR:'YYYY' 形式で年を格納します。
- 範囲:1901 ~ 2155、または 0000。
- 例:書籍の出版年など。
4. 実戦デモとコード例
オンライン書店の想定シナリオを通じて、これらのデータ型の具体的な使い方を確認しましょう。books(書籍)という名前のテーブルを設計します。
-- 'books' テーブルが存在しない場合に作成します。
CREATE TABLE IF NOT EXISTS books (
-- 'book_id' は整数で、プライマリキーとして機能します。
-- INT UNSIGNED により正の整数であることを保証し、IDとして効率的です。
-- AUTO_INCREMENT は新しい本が追加されるたびに一意の連番を自動割り当てします。
-- PRIMARY KEY はこのカラムを各行の一意識別子として指定します。
book_id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-- 'title' は書名を格納します。VARCHAR(255) は柔軟で、様々な長さの書名に対応します。
-- NOT NULL は、書名が必須であり空にできないことを意味します。
title VARCHAR(255) NOT NULL,
-- 'author_first_name' と 'author_last_name' は著者の氏名を格納します。
-- VARCHAR(100) は一般的な氏名の長さをカバーします。
author_first_name VARCHAR(100),
author_last_name VARCHAR(100) NOT NULL,
-- 'publication_year' は YEAR 型を使用し、出版年のみを格納します。
publication_year YEAR,
-- 'price' は正確な通貨数値を保証するために DECIMAL(6, 2) を使用します。
-- 総桁数6桁、小数点以下2桁(例:最大 9999.99 まで)。
-- NOT NULL は価格設定が必須であることを意味します。
price DECIMAL(6, 2) NOT NULL,
-- 'stock_quantity' は在庫数を追跡します。
-- SMALLINT UNSIGNED は最大 65535 までの数量に最適です。
-- DEFAULT 0 により、指定がない場合の初期在庫を 0 に設定します。
stock_quantity SMALLINT UNSIGNED DEFAULT 0,
-- 'genre' は ENUM を使用して固定のカテゴリーに限定し、ストレージを最適化し入力を制限します。
genre ENUM('Fiction', 'Non-Fiction', 'Science', 'Fantasy', 'Mystery', 'Biography') DEFAULT 'Fiction',
-- 'is_available_for_preorder' は TINYINT(1)(BOOLEANの代用)で真偽値を表します。
-- 0 は偽 (false)、1 は真 (true) を示します。
is_available_for_preorder TINYINT(1) DEFAULT 0,
-- 'last_updated_timestamp' は TIMESTAMP 型を使用します。
-- DEFAULT CURRENT_TIMESTAMP により、挿入時のデフォルト値を現在時刻にします。
-- ON UPDATE CURRENT_TIMESTAMP により、行が変更された際に自動的に現在時刻へ更新されます。
last_updated_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- 'description' は書籍の長い紹介文を格納します。
-- TEXT 型は、大量の文字を含む可能性があるコンテンツに適しています。
description TEXT
);次に、データを挿入して、これらのデータ型が実際の数値をどのように処理するか見てみましょう。
-- 'books' テーブルに新しい本を挿入します。
INSERT INTO books (
title,
author_first_name,
author_last_name,
publication_year,
price,
stock_quantity,
genre,
is_available_for_preorder,
description
) VALUES (
'銀河ヒッチハイク・ガイド', -- 書名
'Douglas', -- 著者名
'Adams', -- 著者姓
1979, -- 出版年
12.99, -- 価格 (DECIMAL(6,2))
500, -- 在庫数 (SMALLINT UNSIGNED)
'Fiction', -- ジャンル (ENUM)
0, -- 予約不可 (TINYINT(1) による真偽値)
'コメディSFシリーズ。不条理なユーモアと宇宙旅行ファン必読の一冊。' -- 説明 (TEXT)
);
-- デフォルト値の効果を確認するために、別の本を挿入します。
-- stock_quantity はデフォルトの 0、genre は 'Fiction'、is_available_for_preorder は 0 に設定されます。
-- last_updated_timestamp は挿入時の現在時刻が自動設定されます。
INSERT INTO books (
title,
author_first_name,
author_last_name,
publication_year,
price
) VALUES (
'サピエンス全史',
'Yuval Noah',
'Harari',
2011,
18.50
);
-- 現在予約受付中の本を挿入します。
INSERT INTO books (
title,
author_first_name,
author_last_name,
publication_year,
price,
stock_quantity,
is_available_for_preorder,
genre
) VALUES (
'指輪物語',
'J.R.R.',
'Tolkien',
1954,
25.00,
0, -- 在庫は 0 だが予約可能
1, -- 1 は true を意味する
'Fantasy'
);
-- '銀河ヒッチハイク・ガイド' の在庫数を更新します。
-- この操作により、'last_updated_timestamp' カラムが自動的に更新されます。
UPDATE books
SET stock_quantity = 490
WHERE title = '銀河ヒッチハイク・ガイド';