/note/tech

nullが多いテーブルには異なる情報がまとめられている可能性がある

要約:

■ 1. 記事の目的

  • きっかけ: データベース設計をレビューしていた際に「テーブルにnullが多い時は少し立ち止まってみると良いかもしれない」という話をした
  • 主張: nullが多いテーブルには異なる情報がまとめられている可能性がある

■ 2. nullが多いテーブルの例

  • 携帯電話ショップの顧客管理システム: Customerテーブルの例
  • テーブル構造:
    • 基本情報: id、name、address
    • キャンペーン情報: campaign_id
    • 来店情報: staff_id、sales_note
    • 契約情報: contract_plan_id、phone_number
    • 日時情報: first_came_at、registered_at、cancelled_at
  • 問題: id以外のカラムが全てnull許容

■ 3. nullが増える傾向

  • 主な原因: 本来は異なるモノなのに同じモノとして一つのテーブルにまとめてしまった時、nullableなカラムが増える傾向がある
  • 極端な例: 「Person」と「House」という概念を「Something」テーブルにまとめると、nullableカラムだらけになる
  • 理由:
    • Personには「築年数」なんて情報は存在しない
    • Houseには「血液型」なんて情報は存在しない
    • Somethingテーブルに各データを入れると、存在しない情報がnullになる

■ 4. Customerテーブルの詳細分析

  • registered_at: 契約したcustomerにしか値が存在しない(契約済Customer)
  • cancelled_at: 解約したcustomerにしか値が存在しない(解約済Customer)
  • staff_id: 実際に来店したcustomerにしか値が存在しない(来店済Customer)
  • 問題: 特定の状態のCustomerにしか存在しないカラムが多数存在する

■ 5. 異なる状態を一つのテーブルにまとめる問題

  • クエリの複雑化: 現在契約中の顧客に利用料を毎月請求するには、必ず「WHERE registered_at IS NOT NULL AND cancelled_at IS NULL」を書かなければいけない
  • ミスのリスク: 一箇所でも書き忘れたら、既に解約している過去の顧客や一度来店しただけの顧客に利用料を請求してしまう
  • 責務の移譲: 情報を区別する責務がDBからアプリケーションコード側に委ねられる

■ 6. 追加の問題点

  • リソースとイベントの混在: 「顧客」というリソースと「契約する」「解約する」というイベントが一つのテーブルにまとめられている問題もある
  • 記事の範囲: これは別のトピックに脱線するため本記事では言及しない

■ 7. アプリケーション側に責務が委ねられる弊害

  • 重複実装の必要性: 管理者画面など、同じDBに触れる他のアプリケーションにも同じ処理を書かなければいけない
  • ミスの誘発: 緊急対応時などに、registered_atとcancelled_atを組み合わせる必要があるという暗黙的な知識を持っていないと正しいクエリが書けない
  • 理解の困難性: データベースに格納されたレコードを見ただけでは情報が正しく理解できず、アプリケーション側のロジックも見ないとCustomerが契約中か否か判断できない

■ 8. 推奨される設計方針

  • 基本原則: 本来異なるモノは異なるモノとして別のテーブルに格納した方が良い
  • 例外: 性能上の問題が起きていて、かつその問題がマテリアライズドビューやキャッシュ等で本当に解決できない場合のみ
  • シグナル: nullが多すぎるテーブルは本来異なるモノがまとめられていることを示唆している可能性がある

■ 9. nullが許容されるケース

  • 任意項目の場合: 単純に任意項目だからカラムがnull許容になっているだけで、値がnullだったとしてもレコードの意味は変わらず、アプリケーション上の振る舞いも変わらないケース
  • マッチングアプリの例:
    • とんでもない数の任意選択項目(趣味、性格特性、家族構成など)
    • いくつ選択してもアプリケーション上の振る舞いは変わらない
    • 自分の情報が検索結果に表示されづらくなるだけで、いいねも送れるし、メッセージの送受信も可能
    • 機能的な差はないため、先述の不都合は特に生じない

■ 10. まとめと個人的見解

  • nullの無条件否定ではない: nullだから無条件に悪いのではない
  • 問題となるケース: 特定のカラムがnullか否かによってレコードの意味が変わるようなテーブル(例: cancelled_atがnot nullだと契約済Customerではなく解約済Customerになる)
  • 設計の問題: このようなテーブルは正しく現実の事象をモデル化できていない可能性がある
  • 意見の多様性: nullは無条件に悪いとする意見もあり、nullの許容度に関しては色んな意見がある