勉強日記

チラ裏

理論から学ぶデータベース実践入門 ch5 リレーションの直交性

gihyo.jp


まとめ

  • RDBでしっかりと重複を解消しないのは道具としての使い方を間違っている
    • 正しい答えを得られない
    • 本来の性能を引き出せない

リレーションの直交性(Orthogonality)

  • 複数のリレーション間の重複に関する概念
    • cf. 正規化は1つのリレーション内部の重複に関する概念
  • DB全体から重複を解消する作業ともいえる
  • 一言で言うと「同じ値を含まない」ということ
  • 「同じ属性を含まない」ではない
    • もしそうなら直積しかできない
  • 直交化してない状態とは??

レプリカ

  • 一番わかりやすいやつ
  • 全く同じ構造、同じデータのリレーションが2つ
  • どちらか片方だけ使え

同じ型のリレーション

  • 同じ型だが重複データをもたないやつ
    • 積集合(結合の特殊なもの)が空集合になる
  • 例: 水平分割したテーブル
    • 月ごとにテーブルを分けてるとか
  • SQLに「同じ値を『含まない』」ことを保証する直接的な機能はない
    • cf. FK制約は同じ値を『含む』ことを保証
  • トリガー使うとか?

見出しの一部だけが同じリレーション

  • 判断が難しいやつ
  • リレーションに下記が残っていると、リレーションを直接比較して直交性を判断することはできない
    • 自明ではない関数従属性
    • 暗黙的でない結合従属性
  • 6NFまで無損失分解すれば、タプルの比較で直交性を保証できる
    • 値が重複しているか、あるいは将来的に重複する可能性のある設計であるかが重要
  • 見出しが重複していても、値が直交していれば無理に統合する必要はない

リレーション直交化のための戦略

正規化

  • 直交性の保証のためには6NFまでの正規化が必要
  • 6NFまで正規化するためには5NFまで正規化されていることが必要
  • 実際のテーブルも5NFまでは正規化せよ
    • 6NFは無駄な結合が多くなるため、普通しない

属性(カラム)の名前を統一する

  • よくないもの
    • 同じものを指しているのに異なる名前
      • 見落とす可能性が高まる
    • 異なるものを指しているのに同じ名前
      • 同じものを指している可能性を逐一検証する手間が発生する
  • 命名における注意点
    • 命名規則を統一する
      • 日本語/アルファベット
      • ローマ字/英単語
      • パスカル/スネーク
    • 主語を含める
      • id -> user_id
      • name -> student_name
      • email -> sns_user_email
      • qty -> order_item_qty

アプリケーションの整合性

  • アプリケーションの設計上の問題に起因することがほとんど
    • 異なる2つの機能で同じ意味のデータが必要になった際、
      共通のコンポーネントを設計せず、それぞれ独自にDBにデータ登録した
    • システム統廃合で名寄せをサボった
  • 共通の意味のデータが必要なら、アプリケーションコードもリファクタリングして共通化せよ
    • アプリケーションのロジックからDB側の問題を発見する

すべてを直交化する必要はない

  • 例: 条件ごとにユーザを別々のテーブルに分ける場合
  • 各テーブルが表す条件の意味が完全に独立したものであれば、特に設計上の問題はない
  • 両方の条件を満たす
SELECT user_name
  FROM A
 INNER JOIN B
 USING (user_name)
  • いずれかの条件を満たす
(SELECT user_name
   FROM A)
UNION
(SELECT user_name
   FROM B)

重複を解消することのメリット

  • 正規化・直交化のメリットを振り返りまとめる

異常を防げる

  • 述語論理の天敵
    • Principle of Explosion

必要なデータがどこにあるかが明確になる

  • 直交化されていないDBでは、どのリレーションを対象に演算を行えば良いのか一意に定まらない

クエリの記述が宣言的になる

  • 最低限1NFなら、テーブルはリレーションである
  • テーブルがリレーションなら、クエリは述語論理になる
    • howではなくwhatを記述できる

不要な無損失分解が必要ない

  • 正規化されていない = 結合(JOIN)されている
    • クエリを書くたびに無損失分解(SELECT DISTINCT サブクエリ)が必要になる

複雑な制約が必要ない

  • 正規化されていないと
    • 自明ではない関数従属性が残っている場合
      • 更新時に関数従属性を壊さないよう制約が必要
        • クエリごとのトリガーで集計を行う必要があるだろう
  • 直交化されていないと
    • 重複データをもつ複数のテーブルの同期をとる必要がある
    • FK制約は使えない
      • FK側が部分集合である必要がある
      • 共通でない行が互いのテーブルに含まれる場合は適用不可

アプリケーションのコードに無駄がなくなる

  • DB側で重複を解消しないと、アプリケーション側で異常のチェックが必要になる
    • 異常発生の予防
    • 異常発生時のハンドリング
    • そのテスト
      • 予想以上に甚大

性能が向上する

  • 重複を解消したほうが性能がよくなることも
    • 参照系
      • 結合は増えてしまう
      • が、前述の「クエリを書くたびに無損失分解」する必要がなくなる
    • 更新系
      • 複雑な制約が必要なくなる
      • アプリケーション側でも異常予防・ハンドリングのロジックが不要に
  • 「性能を向上するために非正規化する」のは特殊なケース
    • 一般的には重複があることによりDBの負担は飛躍的に上昇する