DBにおける0と1

DBにおける0と1

~ 文字列型フラグのアンチパターンとは? ~

performed by @motty93
DBにおける0と1

自己紹介

profile image
  • 名前: sasamoto takumi
  • 会社: and roots株式会社
  • 所属: GrowthHack 基盤チーム
  • 職業: Webエンジニア
  • 得意分野: バックエンド開発, インフラ構築
  • 趣味: ゲーム, 個人開発, 筋トレ, ギター
  • GitHub: motty93
performed by @motty93
DBにおける0と1

今日の内容

  1. 0と1フラグとは?
  2. 何が問題なのか?
  3. より良い設計方法
  4. 発展内容
  5. まとめ
performed by @motty93
DBにおける0と1

0 or 1 フラグとは?

  • データベースで「はい・いいえ」を表現するためのフラグ
  • よくある例:
    • is_active: アカウントが有効かどうか
    • is_deleted: 削除されたかどうか
    • is_admin: 管理者権限を持っているかどうか
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    is_active TINYINT(1) -- 0 = false, 1 = true
);

このフラグを「文字列」で管理すると次の問題が発生

performed by @motty93
DBにおける0と1

何が問題なのか?

🚨 1. ストレージ効率が悪い

  • CHAR(1)"0" / "1")や VARCHAR(3) ("yes" / "no") でフラグを管理するケースがある
  • しかし、 TINYINT(1) or BOOLEAN で済むデータより無駄な容量を使う
  • 大量データの検索・処理が遅くなる
SELECT * FROM users WHERE is_active = 'yes';

-> TINYINT(1) or BOOLEANならインデックスを使える

performed by @motty93
DBにおける0と1

何が問題なのか?

🚨 1. ストレージ効率が悪い

BigQueryはスキャンしたデータ量で課金されるため、無駄なストレージ消費はコスト増につながるので注意!!!!!

ちなみに…

  • MySQL: TINYINT(1)型 (8ビット / 1バイト)
  • PostgreSQL: BOOLEAN型 (1ビット)
  • BigQuery: BOOL型 (1ビット)
performed by @motty93
DBにおける0と1

何が問題なのか?

🚨 2. クエリのパフォーマンス低下

  • 数値 (0, 1) は 比較が速い が、文字列 ('yes', 'no') は 遅い
  • 異なるフォーマット(大文字・小文字)での入力ミスが発生
    • "YES" / "yes" / "Yes" など 表記揺れの問題
SELECT * FROM users WHERE is_active = 'YES';

'YES''yes' を区別するかどうかは DB の設定次第でバグのもと!

performed by @motty93
DBにおける0と1

何が問題なのか?

🚨 3. データの整合性が崩れる

  • VARCHAR(3)"yes" / "no" を想定していても、誤ったデータが入ることがある
  • 例: "y", "n", "true", "1" などのバリエーション
  • DB の整合性を保ちにくい
INSERT INTO users (name, is_active) VALUES ('Alice', 'active');

ENUM('yes', 'no') で制限をかけるか、数値型を使うべき!

performed by @motty93
DBにおける0と1

より良い設計の方法

BOOLEAN / TINYINT(1) を使う

  • 最もシンプルでパフォーマンスが良い方法
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    is_active TINYINT(1) NOT NULL DEFAULT 1
);
  • 0 = false, 1 = true のみ許可される
  • ストレージ効率も良く、クエリが最適化される
performed by @motty93
DBにおける0と1

より良い設計の方法

ENUM を使う

  • 文字列を使いたいなら ENUM で明示的に管理
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    status ENUM('active', 'inactive') NOT NULL DEFAULT 'active'
);
  • 許容する値を固定し、表記揺れを防ぐ
  • データの整合性を維持しやすい
performed by @motty93
DBにおける0と1

発展内容

1. フラグの代替設計

  • 0 or 1 の単純な管理ではなく、状態遷移を考慮
  • 例: statusENUM ではなく 外部テーブル で管理
CREATE TABLE user_status (
  user_id INT PRIMARY KEY,
  status_id INT,
  FOREIGN KEY (status_id) REFERENCES statuses(id)
);
performed by @motty93
DBにおける0と1

発展内容

2. データ量が増えたときの設計

  • インデックスの最適化(CREATE INDEX idx_status ON users(status_id))
  • データが増えた時のパーティショニング(ex: created_atでパーティショニング)
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255),
  is_active BOOLEAN,
  created_at TIMESTAMP NOT NULL
) PARTITION BY RANGE (created_at);

CREATE TABLE users_2023 PARTITION OF users FOR VALUES FROM ('2023-01-01') TO ('2023-12-31');
CREATE TABLE users_2024 PARTITION OF users FOR VALUES FROM ('2024-01-01') TO ('2024-12-31');
performed by @motty93
DBにおける0と1

発展内容

3. 論理削除 vs 物理削除

  • is_deletedフラグの問題
    • SELECT毎にWHERE is_deleted = 0を書く必要がある
  • 代替としてdeleted_atカラムを使う(0 or 1 だと歴史がわからない)
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  deleted_at TIMESTAMP NULL
);
  • そもそも物理削除を採用する
    • deleted_usersテーブルに削除されたユーザーを移動するのが理想
performed by @motty93
DBにおける0と1

発展内容

4.ログ・履歴の管理

  • いつ、だれが、どのように変更したかを記録
CREATE TABLE user_status_history (
  id SERIAL PRIMARY KEY,
  user_id INT,
  status_id INT,
  changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
performed by @motty93
DBにおける0と1

発展内容

トピック 重要度 適用ケース
状態遷移を考慮した設計 ✅ フラグの組み合わせが増えている場合
スケールを考慮したデータ設計 ✅ 大規模データで is_active などを検索する場合
論理削除 vs 物理削除 ✅ is_deleted の管理を改善したい場合
監査ログ・変更履歴の管理 🔹 変更履歴を追いたい場合
performed by @motty93
DBにおける0と1

まとめ

✅ 文字列型フラグはストレージ効率・パフォーマンスが悪い
✅ BOOLEAN / TINYINT(1) の方が整合性を保ちやすい
✅ どうしても文字列を使うなら ENUM を活用
✅ DB 設計では、データの型を慎重に選ぼう!
✅ 失敗から学ぶRDBの正しい歩き方を読め

performed by @motty93
DBにおける0と1

ご清聴ありがとうございました!

qr code
performed by @motty93