DB設計(論理設計)のバッドノウハウをラーメンDBで例えてみる
最近、「達人に学ぶDB設計 徹底指南書 ~初級者で終わりたくないあなたへ」の第7章を読んで大変勉強になったので、アウトプットを兼ねてそれぞれのバッドノウハウの内容を箇条書きと独自の例えでここにメモしておこうと思います。
0. 論理設計の「やってはいけない」
- 「戦略の失敗を戦術で取り返すことはできない」
- ダメ設計が生まれる理由は、「何も考えていない」ことによるもの
1. 非スカラ値(第1正規形未満)
配列型は利用しない➡第1正規形を守る
↓ダメな例
ラーメンID | 種類 | トッピング |
---|---|---|
0001 | とんこつ | チャーシュー, 味玉 |
0002 | 醤油 | なし |
0003 | 家系 | のり, ほうれん草, 味玉(うずら) |
- 配列型は標準SQLに盛り込まれているが、この配列型を採用する場合は、データベース内部の設計だけでなく、アプリケーションやミドルウェアとの整合性も考慮しなければならなくなってしまう。
- 原則として配列は、「列」ではなく「行」で表現するべき
↓行持ちテーブル
ラーメンID | 枝番 | トッピング |
---|---|---|
0001 | 1 | チャーシュー |
0001 | 2 | 味玉 |
0003 | 1 | のり |
0003 | 2 | ほうれん草 |
0003 | 3 | 味玉(うずら) |
- 列持ちテーブルに設計することも考えられるが、「列の増減が難しい」や「無用のNULLを使わなくてはならない」といった欠点があるため使いどころが限られる
情報は可能な限り分割して保存するのが良い(ただし意味を壊してはいけない)
- 例えば名前の場合、性と名に分けることができる
(人名)鈴木一郎 ➡ (性、名)「鈴木」「一郎」
(メニュー名)とんこつラーメン ➡ (スープの種類、料理の種類)「とんこつ」「ラーメン」
(電話番号)03-1234-5678 ➡ (市外局番、市内局番、加入者番号)「03」「1234」「5678」
- 理由:分割したものを後で結合することは簡単にできるのに対して、結合された状態のものを後から分割するのは比較的難しいから
- ただし、鈴木を「鈴」と「木」のような意味を破壊してしまうような分解には注意
2. ダブルミーニング
列は変数ではない。一度意味を決めたら変更不可
- 同一の列が二つ以上の意味を持ってしまうと、列の意味が不明確になってしまい、バグの原因となってしまう
ラーメンID | 列1 | 列2 |
---|---|---|
0001 | 太麺 | 硬め |
0002 | 中太面 | 濃いめ |
0003 | 細めん | 多め |
0004 | 細めん | チャーシュー |
↓
ラーメンID | 麺の種類 | 麺の硬さ | スープの濃さ | 油の量 | トッピング |
---|---|---|---|---|---|
0001 | 太麺 | 硬め | |||
0002 | 中太面 | 濃いめ | |||
0003 | 細めん | 多め | |||
0004 | 細めん | チャーシュー |
3. 単一参照テーブル
テーブルにポリモーフィズムはいらない
- 構造的に同じ形をしたいくつかのテーブルをまとめたいという発想から、あらゆるタイプのマスタテーブルを、一つのテーブルに集めたものが単一参照テーブル
- 単一参照テーブルには下記のような利点と欠点がある
-
利点:
- マスタテーブルの数が減るため、ER図やスキーマがシンプルになる
- コード検索のSQLを共通化できる
-
欠点:
- 「コードタイプ」「コード値」「コード内容」の各列とも、必要とされる列長はコード体系によって異なるため、余裕を見てかなり大きめの可変長文字列型で宣言する必要がある
- 一つのテーブルにレコードを集約するため、コード体系の種類と数の多さによっては、レコード数が多くなり、検索のパフォーマンスが悪化する
- コード検索のSQL内でコードタイプやコード値を間違えて指定してもエラーになることがないため、バグに気付きにくい
- ER図がすっきりするとはいっても、ERモデルとしては正確さを欠いており、かえってER図の可読性を下げることになる
-
利点:
- このように利点よりも欠点の方が多く実際に使ってみると大きな問題を抱えている
↓単一参照テーブル(雑多なコード体系の寄せ集め)
コードタイプ | コード値 | コード内容 |
---|---|---|
restaurant_cd | R0001 | 一蘭 |
restaurant_cd | R0002 | 来々軒 |
restaurant_cd | R0003 | 麺場 田所商店 |
location_cd | 815-0081 | 那の川店 ~発祥の店~ |
location_cd | 222-0033 | 淺草 來々軒 |
location_cd | 262-0031 | 武石本店 |
soup_cd | t | とんこつ |
soup_cd | s | しょうゆ |
soup_cd | m | みそ |
4. テーブル分割
- テーブル分割には、水平分割と垂直分割がある
- 水平分割
- レコード単位でテーブルを分割する
- 垂直分割
- 列単位でテーブルを分割する
- 水平分割
- どちらも欠点の方が大きい
- 分割する意味的な理由がない
- 拡張性に乏しい
- 他の代替手段がある
- 水平分割➡パーティション
- 垂直分割➡集約
パーティション
-
テーブルを分割することなく、パーティションキーを軸として物理的に格納領域を分離する
-
これによって、SQLがアクセスするデータ量を1/nに減らせる(nはパーティションの数)
-
一般的にはパーティションはインデックスよりもカーディナリティが小さく、かつ値の変更があまり起きない列をキーにして利用する
-
例えば、下記のようなラーメン注文テーブルがあった場合に、日付に基づいて月ごとにパーティション分割をすることで、各月のデータが独立して管理され、特定の月に対するクエリが効率化される
↓注文テーブル
注文ID | 顧客ID | 種類 | 日付 | 金額 |
---|---|---|---|---|
1 | 101 | 醤油ラーメン | 2024-01-15 | 800 |
2 | 102 | 味噌ラーメン | 2024-01-20 | 850 |
3 | 103 | 塩ラーメン | 2024-02-05 | 750 |
4 | 101 | 豚骨ラーメン | 2024-02-25 | 900 |
5 | 104 | 醤油ラーメン | 2024-03-10 | 800 |
6 | 105 | 味噌ラーメン | 2023-03-15 | 850 |
7 | 106 | 塩ラーメン | 2023-03-20 | 750 |
8 | 107 | 豚骨ラーメン | 2023-03-30 | 900 |
集約
- 集約は二つの種類がある
1. 列の絞り込み
-
単純に保持する列を絞ったテーブルを作成する
-
このようにして作られたテーブルをデータマートやマートと呼ぶ
-
ただしマートを使用する場合は下記二点に注意する
- ストレージ容量
- データ同期
-
多くの場合、マートの更新は1日1回~数回程度の頻度で一括更新(バッチ更新)される
-
しかしこの場合、オリジナルのテーブルとマートのデータ不整合の期間が長くなるため、機能的に問題がないか、要件と照らし合わせながら慎重に検討する必要がある
-
例えば、先ほどの注文テーブルで日付と金額のみが頻繁に使用されるのであれば、下記のようなデータマートを作成しておく
↓注文テーブル(日付と金額のみ)
注文ID | 日付 | 金額 |
---|---|---|
1 | 2023-01-15 | 800 |
2 | 2023-01-20 | 850 |
3 | 2023-02-05 | 750 |
4 | 2023-02-25 | 900 |
5 | 2023-03-10 | 800 |
6 | 2023-03-15 | 850 |
7 | 2023-03-20 | 750 |
8 | 2023-03-30 | 900 |
2. サマリテーブル
- 集約関数によってレコードを集約した状態で保持する
- これにより集約処理を毎回行わずに、事前に集約を行ったテーブルから単純なSELECT文で取得することができる
- 列の絞り込み同様にストレージ容量とデータ同期の問題は注意する必要がある
- 例えば、ラーメンの種類別の売り上げが必要な業務がある場合は下記のようなサマリテーブルを作成しておく
↓ラーメン種類別売上サマリテーブル
種類 | 合計売上 | 注文数 |
---|---|---|
醤油ラーメン | 1600 | 2 |
味噌ラーメン | 1700 | 2 |
塩ラーメン | 1500 | 2 |
豚骨ラーメン | 1800 | 2 |
5. 不適切なキー
可変長文字列は不変性がないためキーには不向き
- 可変長文字列の列は、キーが満たすべき条件である不変性(Stability)を備えていない
- 固定長文字列(CHAR)と混同する
キーには固定長文字列の「コード」列が望ましい
- 固定長文字列と可変長文字列は、同じ文字列を使用しようとしても、物理的には同じ「値」にならないことがほとんどでそれは、固定長文字列が空白による穴埋め(パディング)をするから
6. ダブルマスタ
ダブルマスタとは
- 同じ役割を果たすはずのマスタテーブルが二つ存在するようなケース
ダブルマスタはシステム統廃合で起きることが多い
- もともと別のシステムで利用されていたマスタ同士が、システム統合によって同じドメインに存在するようになった場合に発生する
- データを精査してエンティティの統廃合を行う作業のことを、データクレンジングと呼ぶ
- この作業は、システム統廃合や既存システムの改修時において重要な役割を果たす
- このテータクレンジングを適切に行うことで、ダブルマスタが生じることはない
おわりに
この記事を書いたことによってデータベースの論理設計に関するバッドノウハウへの理解とラーメンへの愛情が深まり大変良い機会となりました🍜
参考書籍:「達人に学ぶDB設計 徹底指南書」ミック. 翔泳社
Discussion