データ型と長さの適切な選定

SQL

~NUMBER・VARCHAR2・DATEの使い分けと設計ミスの回避術~


■ はじめに

Oracleでテーブル設計を行う際に「データ型」と「長さ」をどう定義するかは、パフォーマンス、保守性、拡張性に直結する重要事項です。

こんな設計、していませんか?

・とりあえず VARCHAR2(4000) にしておく
・IDは全部 NUMBER にしておけばいい
・日付は DATE 型で入れているけど、気にしていない

→ これらは後で致命的な設計ミスにつながります。

本記事では、実務でよく使われる3つの型(NUMBER, VARCHAR2, DATE)に絞って、
その特徴、適切な使い方、NG例、最適な設計方法を詳しく解説します。


■ データ型と長さがシステムに与える影響

┌────────────┬──────────────────────────┐
│ 設計ミス │ 影響 │
├────────────┼──────────────────────────┤
│ 不適切な型の使用 │ インデックス効率低下、SQL遅延 │
│ 長さの無制限 │ メモリやI/O負荷増大、行移行・連鎖の誘発 │
│ データ精度の過不足 │ データ不整合、計算ミス、比較演算の誤動作 │
└────────────┴──────────────────────────┘

① NUMBER型:数値を正確に、安全に扱うために

▼ NUMBER(p,s) の意味

  • p:全体の桁数(Precision)
  • s:小数点以下の桁数(Scale)
例:NUMBER(7,2) → 最大99999.99まで格納可能

✅ 使用例と設計方針

項目推奨型理由
IDNUMBER(10)精度明示でインデックス効率UP
金額NUMBER(12,2)固定小数点で集計が正確
割合NUMBER(5,4)0.1234 などの精度を扱える
個数NUMBER(5)小数不要。整数のみの項目に最適化

❌ NG例とその改善案

【NG】NUMBER → 精度無制限。性能劣化や桁あふれリスク  
【NG】NUMBER(38) → 不要に大きく、インデックスが重い
【OK】NUMBER(10) → 適切な範囲に限定

📘 テキスト図:ID列の設計の違い

┌────────────┬────────────┐
│ NG設計 │ OK設計 │
├────────────┼────────────┤
│ EMP_ID NUMBER │ EMP_ID NUMBER(10) │
│ → 精度不明 │ → 範囲が明確 │
│ → 無駄な領域使用 │ → ストレージ効率化 │
└────────────┴────────────┘

② VARCHAR2型:文字列はサイズを見積もる

Oracleの文字列型はほとんどの場合、VARCHAR2 を使用します。

・最大 4000バイトまで(標準構成)  
・NLS設定により1文字 = 1〜3バイトになるケースあり
・無闇に大きく取るとパフォーマンスに悪影響

✅ 設計例(実務対応)

カラム名長さの理由
氏名VARCHAR2(50)姓名の最大を想定 + 苗字が長いケースも考慮
電話番号VARCHAR2(15)ハイフン/国番号を含む場合も想定
メールアドレスVARCHAR2(254)RFC基準最大長に対応

❌ NG例とその影響

【NG】VARCHAR2(4000) → 全項目で使うと表全体が肥大化し、行移行も多発  
【NG】CHAR(50) → 短い値でも50バイト確保。ストレージが無駄

📘 テキスト図:長すぎるVARCHAR2の問題点

┌──────────────┬─────────────┐
│ NG設計 │ OK設計 │
├──────────────┼─────────────┤
│ EMAIL VARCHAR2(4000) │ EMAIL VARCHAR2(254) │
│ → 実質使わない長さ │ → メールの最大長に基づいた適正設計 │
│ → 行移行、メモリ浪費 │ → インデックス効率も良好 │
└──────────────┴─────────────┘

③ DATE型:時刻まで格納されることに注意

OracleのDATE型は「日付+時分秒」を保持します。

-- SYSDATEの中身
2025-07-05 14:23:41

✅ よくある利用パターン

項目注意点
登録日時DATESYSDATE or SYSTIMESTAMP を使用
生年月日DATE時刻不要なら TRUNC を検討
有効期限DATE日付だけで判断していると時刻で不一致になる

❌ NG例と対策

-- 意図せず時刻が含まれると条件ミスが発生
SELECT * FROM T_USER
WHERE JOIN_DATE = TO_DATE('2025-07-05', 'YYYY-MM-DD');

-- → 実際は「2025-07-05 14:23:41」なのでヒットしない

-- 解決策:
SELECT * FROM T_USER
WHERE TRUNC(JOIN_DATE) = TO_DATE('2025-07-05', 'YYYY-MM-DD');

📘 テキスト図:DATE型でよくあるトラブル

┌─────────────┬────────────────────┐
│ 問題 │ 対策 │
├─────────────┼────────────────────┤
│ 時刻が一致せず検索漏れ │ TRUNC(JOIN_DATE) を使用 │
│ 日付だけ扱うつもりが誤解される │ コメントで時刻格納を明記する │
└─────────────┴────────────────────┘

■ ストレージとパフォーマンスへの影響まとめ

┌────────────┬──────────────┬──────────────┐
│ データ型 │ NG設計例 │ パフォーマンス影響 │
├────────────┼──────────────┼──────────────┤
│ NUMBER │ NUMBER(38) │ 索引肥大、スキャン非効率 │
│ VARCHAR2 │ VARCHAR2(4000) │ 行移行、I/O過多 │
│ DATE │ 時刻混入の比較誤り │ 検索漏れ、結合ミス │
└────────────┴──────────────┴──────────────┘

■ チェックリスト(設計時に見直したい)

項目チェック内容
データ型NUMBER, VARCHAR2, DATE 以外は本当に必要か?
長さ指定無駄に大きすぎないか?文字列の使用想定は根拠があるか?
精度指定金額・数量は桁数の上限と小数桁が定まっているか?
NULL制約必須項目にNOT NULLをつけているか?

■ まとめ

データ型と長さの選定は、「とりあえず」で決めてはいけません。
現場のデータ要件とパフォーマンス要件の両方を理解した上で、設計意図を明示することが求められます。

  • NUMBER は精度と用途に合わせて (p,s) を設定
  • VARCHAR2 は「想定最大長 + マージン」で設計する
  • DATE 型は時刻も含まれることを意識して使い分ける

これらを意識した設計により、SQLの安定性、索引効率、ストレージ節約、保守性のすべてが向上します。

[参考]
Oracle Database データベース開発ガイド 19c

コメント

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