【MySQL】MySQL固有のデータ型(ENUM・SET・TEXT系の使い分け)

MySQL

こんにちは、かつコーチです。

アーキテクチャ編では、ストレージエンジンや文字コードの仕組みを解説しました。

ここからはデータ型編として、MySQL固有のデータ型に絞って解説します。

INT・VARCHARといった基本的な型はSQL基礎編で扱い済みのため、ここではMySQLならではのENUMSET・TEXT系の型を中心に見ていきます。

ENUM型:決まった選択肢から1つを選ぶ

ENUMとは?

ENUM型とは、あらかじめ決めた選択肢の中から、1つだけ値を選んで格納できるMySQL固有のデータ型です。

CREATE TABLE orders (
  id INT PRIMARY KEY AUTO_INCREMENT,
  status ENUM('未発送', '発送済み', 'キャンセル') NOT NULL DEFAULT '未発送'
);

このように定義すると、status列には未発送発送済みキャンセルのいずれかしか入れられなくなります。

-- これは成功する
INSERT INTO orders (status) VALUES ('発送済み');

-- 選択肢にない値を入れようとすると警告またはエラーになる
INSERT INTO orders (status) VALUES ('保留中');

内部的にはENUMの各値は整数として保存されるため、VARCHARで同じ制御をするよりもストレージ効率がよいという特徴があります。

ENUMとVARCHAR+CHECK制約、どちらを選ぶか

MySQL 8.0.16以降はCHECK制約もサポートされたため、選択肢の制限はVARCHARCHECK制約でも実現できます。

方法メリットデメリット
ENUMストレージ効率がよい、定義がシンプル選択肢の追加・削除にALTER TABLEが必要
VARCHAR + CHECK制約制約の変更が比較的柔軟ENUMよりストレージ効率が劣る

選択肢の変更が今後ほぼ発生しない(曜日、都道府県など)場合はENUM、将来的にステータスの種類が増減しそうな場合はVARCHAR+CHECK制約、あるいはマスタテーブルを別に用意する設計の方が安全です。

私が実務で担当した受注管理システムでは、当初ENUMでステータスを管理していましたが、営業フローの変更でステータスの種類が頻繁に追加されるようになり、最終的にはステータスマスタテーブルへの切り出しに設計変更した経験があります。

「将来変わる可能性がある選択肢」をENUMで固定してしまうと、後からの変更コストが高くなる点は覚えておくとよいでしょう。

SET型:複数の選択肢を同時に選ぶ

SETとは?

SET型は、ENUMと似ていますが、あらかじめ決めた選択肢の中から複数選んで格納できる点が異なります。

CREATE TABLE members (
  id INT PRIMARY KEY AUTO_INCREMENT,
  interests SET('プログラミング', 'デザイン', 'マーケティング', '経営')
);
INSERT INTO members (interests) VALUES ('プログラミング,デザイン');

このように、カンマ区切りで複数の値を1つの列にまとめて保存できます。

SET型を使うべきか、中間テーブルにすべきか

SET型は手軽ですが、実務ではあまり積極的に使われない型でもあります。

理由は、SET型の値に対して「プログラミングを選んだ人だけ抽出する」といった検索を行う際、FIND_IN_SET関数など専用の書き方が必要になり、通常のインデックスが効きにくいためです。

SELECT * FROM members WHERE FIND_IN_SET('プログラミング', interests);

会員の興味関心のように「1人が複数の値を持ちうる」データは、SET型よりもmembersinterests中間テーブル(多対多の関係を表現するテーブル)で正規化して管理する方が、検索性能もインデックスの効きも良くなります。

SET型は「選択肢が少なく、複雑な検索をしない」場合に限定して検討するのが現実的です。

TEXT系データ型の使い分け

VARCHARとTEXTの違い

VARCHARは最大長を指定して使う可変長文字列ですが、格納できる長さには実用上の上限があります。

より長い文章を保存したい場合は、TEXT系のデータ型を使います。

データ型最大サイズ目安主な用途
VARCHAR(n)最大65,535バイト(実質は文字セットや行サイズに依存)氏名、タイトルなど短い文字列
TEXT約65KBブログ本文、コメントなど中程度の長文
MEDIUMTEXT約16MB長文記事、大きめのJSON文字列など
LONGTEXT約4GB非常に長いログやHTML全文など

なぜVARCHARだけで済ませないのか

❌ Before:とりあえずVARCHAR(65535)で長文を受け止める

CREATE TABLE articles (
  id INT PRIMARY KEY AUTO_INCREMENT,
  body VARCHAR(65535)
);

VARCHARは最大長を指定できますが、実際には1行あたりの合計サイズ制限(デフォルトで65,535バイト)を他の列と分け合う形になります。

私が過去にレビューしたテーブル定義で、複数の列をすべて大きめのVARCHARにした結果、Row size too largeというエラーでテーブルが作成できないというトラブルに遭遇したことがあります。

✅ After:長文が想定される列はTEXT系を使う

CREATE TABLE articles (
  id INT PRIMARY KEY AUTO_INCREMENT,
  title VARCHAR(255),
  body TEXT
);

短いタイトルはVARCHAR、長さが可変で伸びる可能性のある本文はTEXT、というように用途に応じて型を使い分けることで、行サイズの制限に悩まされにくくなります。

ブログ記事本文程度であればTEXT(約65KB)で足りることがほとんどですが、大量のログやHTMLをまるごと保存するような用途ではMEDIUMTEXTを検討します。

応用:CHAR型が向いているケース

固定長データにはCHARを使う

CHAR(n)は常に指定した長さでデータを保存する固定長のデータ型です。

郵便番号やISOの国コードのように、長さが常に一定のデータにはCHARが適しています。

CREATE TABLE addresses (
  id INT PRIMARY KEY AUTO_INCREMENT,
  postal_code CHAR(7)
);

長さが一定なデータは、可変長のVARCHARよりCHARの方がわずかに処理効率がよいとされていますが、体感できるほどの差になるのは大量データを扱う場合に限られます。

「長さが常に一定かどうか」を基準に、CHARとVARCHARを使い分けると設計判断がしやすくなります。

まとめ

この記事のポイント

  • ENUMは1つの選択肢、SETは複数の選択肢を格納するMySQL固有の型
  • 選択肢が今後変わりうる場合は、ENUMよりVARCHAR+CHECK制約やマスタテーブルを検討する
  • SET型は検索性能の面で不利になりやすく、中間テーブルへの正規化も選択肢に入れる
  • 長文にはTEXT系、固定長データにはCHARを使い分ける

次に読むべき記事

決まった選択肢や固定長データの扱い方が分かったところで、次は半構造化データを扱うJSON型について解説します。

→ 次の記事:JSON型の使い方:半構造化データをMySQLで扱う

タグ: MySQL, 中級者向け, データ型

タイトルとURLをコピーしました