はじめに
前回の記事「データベース設計入門 第6回:主キー・外部キー・UNIQUE制約を設計する
」では、主キー・外部キー・UNIQUE制約・NOT NULL制約などの制約について説明しました。
テーブルやカラムを設計し、それぞれの関係や制約を決めたら、次に考えたいのがカラムのデータ型です。
例えば、次のようなデータを保存するとします。
- 年齢
- 生年月日
- 金額
- フラグ
- 状態
このとき、
年齢 → INT?
生年月日 → DATE?
金額 → INT? DECIMAL?
フラグ → BOOLEAN?
状態 → VARCHAR? ENUM?
といったように、「どのデータ型を使えばよいのだろう?」と迷うことがあります。
特に、データ型についてあまり意識したことがないと、
よく分からないから、とりあえずVARCHARにしておこう
となってしまうこともあります。
確かに文字列にしておけば、多くの値を保存できます。
しかし、「保存できる」ことと「そのデータに適した型である」ことは別の話です。
今回は、具体的なカラムを例にしながら、実際のデータベース設計でデータ型をどのように考えればよいのかを見ていきます。
この記事の対象者
この記事は、次のような人を対象としています。
- エンジニアだけれど、データベースについてあまり詳しくない
- テーブル設計の経験がまだ少ない
-
INTやVARCHARは知っているが、どう使い分けるのか分からない - とりあえず
VARCHARにしてしまうことが多い -
DECIMALやBOOLEAN、ENUMをいつ使えばよいのか分からない
なお、データ型の名前や仕様は、MySQL、PostgreSQL、SQLiteなどのDBMSによって異なる場合があります。
この記事では、特定のDBMSだけに依存する細かな仕様よりも、
「どのような性質のデータなのかを考えて型を選ぶ」
という基本的な考え方を中心に説明します。
データ型とは何か
データ型とは、簡単に言えば、
「そのカラムに、どのような種類の値を保存するのか」
を決めるものです。
例えば、
年齢 → 整数
名前 → 文字列
生年月日 → 日付
価格 → 数値
といった具合です。
代表的なデータ型には、次のようなものがあります。
| 種類 | データ型の例 | 保存するデータ |
|---|---|---|
| 整数 | INT |
1、100、-10など |
| 小数 |
DECIMAL / NUMERIC
|
100.50など |
| 文字列 | VARCHAR |
名前、メールアドレスなど |
| 日付 | DATE |
2026-09-16など |
| 日時 |
TIMESTAMP など |
日付+時刻 |
| 真偽値 | BOOLEAN |
true / false |
| 列挙値 | ENUM |
決められた候補の中から1つ |
ただし、
数字だからINT
文字が入るからVARCHAR
と機械的に決めればよいわけではありません。
重要なのは、
その値が業務上何を意味しているのか
まで考えることです。
「とりあえずVARCHAR」にすると何が問題なのか
データ型に迷ったとき、
age VARCHAR(10)
birthday VARCHAR(20)
price VARCHAR(20)
のように、すべて VARCHAR にしてしまえば楽に見えるかもしれません。
文字列であれば、かなり自由にデータを格納できるからです。
しかし、この「自由に格納できる」という性質が問題になることがあります。
例えば、年齢を VARCHAR にすると、
20
35
十八歳
不明
abc
といった値まで保存できてしまいます。
生年月日を文字列にすると、
2026-09-16
2026/09/16
9月16日
September 16
不明
のように、表記方法がバラバラになる可能性もあります。
金額であれば、
1000
1,000円
約1000円
無料
といった値が混在するかもしれません。
このような状態になると、プログラム側で
この値は本当に数字なのか?
この日付はどの形式なのか?
と毎回確認しなければなりません。
また、本来数値であるデータを文字列として保存すると、数値としての計算や比較を行う際にも変換が必要になります。
つまりデータ型は単に、
「データを保存するための形式」
ではありません。
「このカラムには、この種類のデータしか入らない」というルールを表現するもの
でもあります。
前回の記事で説明した制約と同じように、適切なデータ型を設定することで、不正なデータがデータベースに入る可能性を減らすことができます。
具体例からデータ型を考えてみる
それでは、具体的なカラムを使って考えてみましょう。
今回は次の5つを例にします。
年齢
生年月日
金額
フラグ
状態
年齢 → INT?
まずは年齢です。
例えば、
age
というカラムがある場合、
age INT
としたくなります。
年齢は、
18
25
40
のような整数なので、データ型だけを考えるなら INT は自然です。
しかし、ここではもう一歩考える必要があります。
そもそも「年齢」を保存する必要があるのか
例えば、2026年9月16日時点で30歳の人がいたとします。
データベースに、
age = 30
と保存しても、1年後には31歳になります。
しかし、データベースに保存されている値が自動的に、
30 → 31
へ変わるわけではありません。
そのため、
age
そのものを保存するよりも、
birth_date
として生年月日を保存し、必要なときに年齢を計算する方法が適していることがあります。
例えば、
birth_date = 1996-04-10
と保存しておけば、現在の日付との差から年齢を求めることができます。
つまり、
年齢だからINT
とすぐに決めるのではなく、
そもそもこの値はデータベースに保存すべきものなのか?
まで考えることが重要です。
年齢そのものを保存したいケースもある
一方で、年齢を保存することが必ず間違いというわけではありません。
例えば、
商品購入時点の年齢
を記録したい場合です。
purchase_age = 25
という値は、その後ユーザーが26歳になっても変わりません。
これは、
「現在の年齢」ではなく「購入時点の年齢」
という履歴情報だからです。
このようなケースであれば整数として保存する意味があります。
ポイント
データ型を決める前に、
この値は現在の状態を表すのか?
履歴として残す値なのか?
別の値から計算できないか?
を考えてみましょう。
生年月日 → DATE?
次に生年月日です。
birth_date
であれば、
birth_date DATE
とするのが基本的な考え方です。
生年月日は文字列のようにも見えます。
例えば、
1990-05-10
という値は VARCHAR にも保存できます。
しかし、生年月日は単なる文字列ではなく、日付という意味を持つデータです。
そのため、日付を扱えるDBMSであれば DATE のような型を使用する方が自然です。
日付型として管理しておけば、
2026-09-16より前
といった日付としての比較や、
○年○月のデータを取得する
といった処理がしやすくなります。
また、
2026-99-99
のような明らかに日付として不自然な値を文字列として自由に扱う設計も避けやすくなります。
DATEと日時は別物
ここで注意したいのが、
生年月日
と、
登録日時
では必要な情報が違うということです。
生年月日であれば、
1990-05-10
のように年月日だけ分かれば十分でしょう。
一方、
created_at
のような登録日時であれば、
2026-09-16 14:30:15
のように時刻まで必要です。
つまり、
birth_date → DATE
created_at → TIMESTAMPなど
のように使い分けます。
DATETIME や TIMESTAMP など日時を扱う型の名称や挙動はDBMSによって違いがあるため、実際には利用するDBMSの仕様も確認しましょう。
金額 → INT? DECIMAL?
金額は、少し判断が難しくなるデータです。
例えば商品の価格として、
1000円
を保存するとします。
日本円だけを扱い、1円未満の金額が存在しないのであれば、
price INT
のように整数として持つこともできます。
1000円 → 1000
1980円 → 1980
という形です。
しかし、扱う金額によっては小数が必要になります。
例えば、
10.50ドル
のような金額です。
このような場合には、
price DECIMAL(10, 2)
などの固定小数点型を検討します。
DECIMAL(10, 2) であれば、一般的には
全体で最大10桁
そのうち小数点以下2桁
という意味になります。
例えば、
12345678.90
といった値を表現できます。
金額にFLOATやDOUBLEを使う?
小数を扱う型には、
FLOAT
DOUBLE
などもあります。
しかし、これらは一般に浮動小数点数として扱われ、値によっては内部表現の都合で近似値になります。
一方、DECIMAL や NUMERIC は正確な10進数を扱いたい用途に向いています。
そのため、
金額
料金
残高
税額
のように、誤差を避けたい値については DECIMAL / NUMERIC などを検討します。
逆に、
計測値
科学技術計算
など、近似値を許容して広い範囲の数値を扱いたい場合には、浮動小数点型が適していることもあります。
INTかDECIMALかは「金額だから」だけでは決まらない
つまり、
金額 → DECIMAL
と決め打ちするのではなく、
小数が存在するか
扱う通貨は何か
最大でどれくらいの金額になるか
まで考える必要があります。
例えば日本円のみで小数を扱わないのであれば、
price INT
あるいは扱う金額の範囲によって、
price BIGINT
とする設計も考えられます。
フラグ → BOOLEAN?
次はフラグです。
例えば、
有効 / 無効
削除済み / 未削除
公開 / 非公開
のように、2つの状態だけを表すカラムです。
is_active
is_deleted
is_public
などのカラム名がよく使われます。
このような値は、
is_active BOOLEAN
のような真偽値として表現すると意味が分かりやすくなります。
TRUE → 有効
FALSE → 無効
という形です。
文字列で、
"有効"
"無効"
と保存することもできますが、
"有効"
"無効"
"停止"
"不明"
のように、本来想定していない値が入り込む余地が増えてしまいます。
BOOLEANであれば、
「この値はYESかNOかを表している」
という意図も分かりやすくなります。
BOOLEANはDBMSによって扱いが違う
ここは少し注意が必要です。
BOOLEAN という型の扱いはDBMSによって異なります。
例えば、PostgreSQLには真偽値を扱う boolean 型があります。
一方、MySQLでは BOOLEAN / BOOL は TINYINT に対応する型として扱われます。
SQLiteにも独立したBOOLEANのストレージクラスはなく、一般的には 0 と 1 を使って表現します。
そのため、
フラグには論理的にはBOOLEANが適している
と考えた上で、実際の定義方法については利用しているDBMSの仕様を確認しましょう。
状態 → VARCHAR? ENUM?
最後は「状態」です。
例えば注文に、
status
というカラムがあり、
受付
支払い済み
発送済み
キャンセル
という状態を持つとします。
これを、
status VARCHAR(20)
とする方法があります。
例えば、
pending
paid
shipped
cancelled
といった文字列を保存します。
しかし、単純な VARCHAR だけでは、
payed
shiped
canceled
abc
のような想定外の文字列が登録される可能性があります。
そこで候補になるのが ENUM です。
例えば、ENUM型をサポートしているDBMSでは、
status ENUM(
'pending',
'paid',
'shipped',
'cancelled'
)
のように、
あらかじめ決められた値の中から1つだけを選ぶ
という設計ができます。
これなら、
pending
paid
shipped
cancelled
以外の状態を登録できないようにできます。
ENUMなら必ず良いのか
ここでも、
状態だからENUM
と機械的に決めればよいわけではありません。
例えば、将来、
返金済み
返品処理中
配送保留
など、新しい状態が頻繁に増えるシステムだったとします。
ENUMの候補を変更するにはデータベースの定義変更が必要になるため、状態の追加頻度やDBMSの仕様によっては扱いづらくなることがあります。
また、ENUMそのものの仕様もDBMSによって異なります。
そのため、
VARCHAR + CHECK制約
という方法も考えられます。
例えば、
status VARCHAR(20)
CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled'))
とすれば、文字列として保存しつつ、登録可能な値を制限できます。
さらに、状態自体が、
状態名
表示順
説明
有効・無効
など複数の情報を持つようになれば、
statuses
のようなマスターテーブルとして分離する設計も考えられます。
つまり状態については、
VARCHAR
ENUM
VARCHAR + CHECK制約
別テーブル
など複数の設計方法があります。
大切なのは、
値の種類がどの程度固定されているのか
を考えることです。
「値が何に見えるか」ではなく「何を表しているか」で考える
ここまでの例を整理してみましょう。
| データ | 候補 | 考えるポイント |
|---|---|---|
| 年齢 | INT |
そもそも保存する必要があるか |
| 生年月日 | DATE |
文字列ではなく日付として扱う |
| 金額 |
INT / BIGINT / DECIMAL
|
小数、通貨、最大金額 |
| フラグ |
BOOLEAN相当 |
本当に2つの状態だけなのか |
| 状態 |
VARCHAR / ENUMなど |
値の種類が固定されているか |
重要なのは、
数字に見える → INT
文字に見える → VARCHAR
という見た目だけで判断しないことです。
例えば、
郵便番号
電話番号
商品コード
には数字だけが使われることがあります。
しかし、
09012345678
という電話番号を2倍したり、
1000001 + 1000002
と郵便番号同士を足したりすることはありません。
これらは数値というより、
数字を使って表現された識別情報
です。
そのため、文字列として扱う方が自然な場合があります。
逆に、
"1000"
と文字列で保存できたとしても、それが商品の価格なのであれば数値型の方が自然です。
データ型を決めるときは、
このデータを使って、どのような処理を行うのか?
を考えてみましょう。
データ型を決めるときに確認したいこと
実際のテーブル設計では、次のような順番で考えると整理しやすくなります。
1. その値は何を表しているのか
まず業務上の意味を確認します。
例えば、
30
という値だけを見ても、
年齢
数量
価格
ステータスコード
のどれなのか分かりません。
何を表しているかによって、適切な型は変わります。
2. どのような値が入るのか
例えば、
整数だけなのか
小数もあるのか
日付なのか
文字列なのか
TRUE / FALSEだけなのか
を考えます。
3. 値の範囲はどのくらいか
数値型の場合、
最大で100程度なのか
数百万なのか
数十億を超える可能性があるのか
といった範囲も重要です。
必要以上に大きな型にする必要はありませんが、将来扱う可能性のある値まで考えて選択します。
4. 候補が決まっているか
例えば、
未処理
処理中
完了
のように値が限定されているのであれば、
ENUM
CHECK制約
マスターテーブル
などで値を制限できないか考えます。
5. 計算や比較をするか
例えば、
価格を合計する
日付の前後を比較する
数量を集計する
のであれば、それらの処理に適した型を選ぶ必要があります。
単に「保存できる」という理由だけで VARCHAR にしないことが重要です。
6. 利用するDBMSではどのように扱われるか
最後に、実際に利用するDBMSの仕様を確認します。
例えば、
BOOLEAN
ENUM
日時型
自動採番
などは、DBMSによって仕様や書き方が異なる部分です。
そのため、
設計段階ではデータの性質を考え、実装段階ではDBMSの仕様に合わせる
と考えるとよいでしょう。
データ型もデータベースのルールの一つ
前回は、
PRIMARY KEY
FOREIGN KEY
UNIQUE
NOT NULL
CHECK
などの制約について説明しました。
データ型も、考え方としてはそれらとよく似ています。
例えば、
age INT
とすることで、
ageには整数を入れる
というルールを表します。
birth_date DATE
とすることで、
birth_dateには日付を入れる
というルールを表します。
つまり、
データ型
制約
リレーション
などを組み合わせることで、
「このデータベースでは、どのようなデータを正しいデータとして扱うのか」
を設計していくことになります。
アプリケーション側でも入力チェックは必要ですが、
アプリケーションでチェックしているから、データベース側は何でもよい
と考えるのではなく、データベース側にも適切なルールを持たせることが大切です。
まとめ
今回は、カラムのデータ型について説明しました。
データ型を決めるときに重要なのは、
「とりあえず保存できる型」を選ぶのではなく、そのデータが何を意味するのかを考えること
です。
今回の例であれば、
年齢
→ INTにする前に、そもそも保存すべきか考える
生年月日
→ 日付なのでDATEを検討する
金額
→ 小数の有無などによってINT・BIGINT・DECIMALなどを検討する
フラグ
→ 2つの状態ならBOOLEAN相当を検討する
状態
→ VARCHAR、ENUM、CHECK制約、マスターテーブルなどを検討する
となります。
そして、特に避けたいのが、
よく分からないから全部VARCHARにする
という設計です。
VARCHAR が悪いわけではありません。
名前やメールアドレス、コードなど、文字列として扱うべきデータには当然 VARCHAR が適しています。
問題なのは、
「なぜこの型なのか」を考えずに決めてしまうこと
です。
データベース設計では、
このカラムには何が入るのか
どのような操作をするのか
どこまでの値を許可するのか
将来どのように変化する可能性があるのか
を考えながらデータ型を選びましょう。
データ型を適切に設計することも、壊れにくく扱いやすいデータベースを作るための重要な要素です。
データベース設計入門シリーズ
これまでの記事はこちらです。