DB設計の進め方
重要な考え方
情報とデータ
データ
単なる事実の値。
これを永続化して蓄えるのがRDBの目的。
情報
データから生み出される、意味や目的のあるもの。
SQLで出力する。
更新(UPDATEとDELETE)は複雑さを生む
- そもそも集合論的に更新はない。
- 更新にはテーブルを見るだけではわからない複雑なルールを伴うため、極力更新を避けるようにDB設計すべき。
- この価値観は、不足しているエンティティや属性の発見にもつながる。更新で対応していたことが、実はエンティティや属性が不足していたことが原因ということが発見されうるということ。
- 更新がなければデータが失われることはない。
- 更新を許す = 履歴や遷移の過程を残さなくてもいい ということ。そういう場合には更新をしてもよい。
典型的なアンチパターンを踏んでいないか確認する
システムの品質はDB設計で決まる
DBモデルの変更は、コードベースの修正に比べて非常に改修コストが高い。
これはデータのフォーマットがプログラムを決めているため(DAOの大原則)。
システムの品質はDB設計で決まる、という気概を持ちながら、適切にDB設計をする必要がある。
非正規化にはリスクが伴う
- 非正規化は冗長化をすることに他ならず、更新不整合を起こす原因となる。
- 検索のパフォーマンスは上がるが、更新のパフォーマンスは低下する。
- データのリアルタイム性が失われる。
One Fact In One Place
一つの事実は一つと場所にのみ存在する。 これを実現するための指針が正規化。
設計の種類
論理設計
物理的な制約を無視した、理想的な設計。
まずはこれを行う。
以下4工程で構成される。
- エンティティ抽出
- エンティティ定義
- 正規化
- ER図作成
物理設計
パフォーマンスやハードウェアの性能(e.g. CPU, ストレージ etc...)など、現実問題への対処方法を考える。
以下5工程で構成される。
- テーブル定義
- インデックス定義: パフォーマンス向上のため
- ハードウェアのサイジング
- ストレージの冗長構成
- ファイルの物理配置
論理設計
前提知識
エンティティの種類
- リソース: 名詞で表されるエンティティ。イベント以外のエンティティと捉えたほうが楽かも。
- イベント: 一般的には動詞で表されるエンティティで、属性に「~する」とつけて自然と成り立つもの。ただし、必ずしもこの方法で判別はできない。属性に1つの日時を持つ(ここで言う日時とは、DateやDateTime型のカラムすべてという意味ではない。アクティビティが行われる日時のことを指している。)。
関数従属
xを決めればyが決まる関係(y=f(x))。
ここで言うxが主キーにあたり、yはその他の属性にあたる。
主キーは単数 or 複数で成る。
部分関数従属
主キーの一部を決めれば値が決まる関係。
それゆえ、主キーが複数あるテーブルのみでありうる。
推移的関数従属
A→B, B→CゆえにA→Cという推移的従属性がある関係。 (A, B, Cは属性)
エンティティの抽出
要求文章内の5W1Hに下線を引く。
これらがエンティティや属性の候補になる。
ポイントは、端折らずにすべての要求を文字に起こし、この作業を行うこと。
ここでエンティティや属性の候補に抜けがあると、後続に誤りが伝搬するため注意する。
何はともあれ項目を全て列挙すれば良い。 この際に、実際の値も書き添えておくと、データのイメージが湧きやすくてよい。 楽々ERDレッスルp.11
エンティティ定義
- 属性(カラム)の抽出: 主キーも含む。
- 制約条件: NOT NULL制約, UNIQUE制約, CHECK制約(値の範囲)
正規化
データの冗長性を排除し、一貫性と効率性を保持させる。
第三正規化までで基本的に問題ない。
正規化とパフォーマンスはトレードオフであり、正規化を勧めると検索パフォーマンスは下がる。
パフォーマンスが下がるからといって、非正規化をすることはご法度。
非正規化をする = データを冗長化させる ことに異ならなく、それによってデータを整合性を持って保管する難易度が上がる。
非正規化はあくまで最終手段、他の手が全てだめだった時にだけ行う(基本行わないスタンスでいるとよさそう)。
注意点は、業務の視点からの正規化を行うということ。 なんでも重複するものをDRYにすることが誤りなように、正規化も思考停止にやるのは誤り。 あくまで業務的な意味合いで重複しているデータを排除する意味で正規化を行う。 一見同一項目と考えられる属性についても、値をよくみると一つにまとめてはいけないことがある。 このような正規化を行うと、結果としてプログラミング言語側に負担が強いられることになる。
第一正規化
1レコード1カラムに複数の値が入っているものを排除し、1レコードの1カラムに1つの値となるようにする。
第二正規化
部分関数従属を解消(=別テーブルに切り出す)し、関数従属のテーブルだけが存在するようにする。
第三正規化
推移的関数従属を解消(=別テーブルに切り出す)し、関数従属のテーブルだけが存在するようにする。
イベントとリソースを正しく設計
イベントエンティティ
イベントには、1つの日時属性しか持たないようにする。
日付が複数ある=複数のイベントを含む → 複数回更新が発生する(イベントの発生ごとにレコードに更新が走るため) → 複雑さを招くから。
逆に1つの日時属性しか持たないようにすれば、イベントエンティティの性質上、エンティティには1回のINSERTしか行われない。
リソースエンティティ
リソースに隠れたイベントを抽出する。
イベントがないかをよく確認する(アクティビティの日時を管理する属性が含まれないか etc...) or 今一度要求を文章化して、エンティティを考え直すところからやり直してみる。
バックアップ設計, リカバリ設計
達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ: p.52~66
その他、DB設計のテクニック
https://ken1min86.hatenablog.com/entry/2023/06/23/182424
アンチパターン
https://ken1min86.hatenablog.com/entry/2023/06/23/182527
参考
- 達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ
- イミュータブルデータモデル
- 楽々ERDレッスン