テーブルの正規化を理由にテーブルを増やす提案をしたら嫌がられた記憶がある。
「なるべくテーブルを増やしたくない。たぶんカオスになる」という考えっぽいけど、これは正しく設計されずに不要なテーブルを作成されまくってしまったケースの経験が元になっている気がした。(確かにしんどい)
しかし、正しく正規化して適切にテーブルを分割しないと、これはこれで後々かなりめんどくさい事になるということを書いてみる。
サンプルケース
既存テーブル
商品購入時の処理に使う、購入テーブルと購入詳細テーブルがあったとする。
購入テーブル
id | 購入者id | 購入日 |
---|---|---|
1 | 1 | 2020-01-01 |
2 | 2 | 2020-01-02 |
購入詳細テーブル
id | 購入id | 商品id | 個数 |
---|---|---|---|
1 | 1 | 1 | 10 |
2 | 1 | 2 | 1 |
3 | 2 | 1 | 5 |
おまけ対応テーブル案1
ある日、売上UPの施策としておまけ商品をつける事になった。
購入おまけ詳細テーブルを用意することを検討した。
購入テーブル
id | 購入者id | 購入日 |
---|---|---|
1 | 1 | 2020-01-01 |
2 | 2 | 2020-01-02 |
購入詳細テーブル
id | 購入id | 商品id | 個数 |
---|---|---|---|
1 | 1 | 1 | 10 |
2 | 1 | 2 | 1 |
3 | 2 | 1 | 5 |
購入おまけ詳細テーブル
id | 購入id | 商品id | 個数 |
---|---|---|---|
1 | 1 | 10 | 1 |
2 | 2 | 11 | 1 |
おまけ対応テーブル案2
しかし、よく見ると購入詳細テーブルと購入おまけ詳細テーブルのデータ内容は同じであるのに気が付き、テーブルを増やしたくないから購入詳細テーブルに追加データをもたせて対応することになった。
購入テーブル
id | 購入者id | 購入日 |
---|---|---|
1 | 1 | 2020-01-01 |
2 | 2 | 2020-01-02 |
購入詳細テーブル
id | 購入id | 商品id | 個数 | 種別id |
---|---|---|---|---|
1 | 1 | 1 | 10 | 1 |
2 | 1 | 2 | 1 | 1 |
3 | 2 | 1 | 5 | 1 |
4 | 1 | 10 | 1 | 2 |
5 | 2 | 11 | 1 | 2 |
※種別id = 1は通常商品
種別id = 2はおまけ商品
ありがちな気がする。。
何が問題?
購入商品とおまけ商品は本質的に違うものである。たまたま今は保持データが似てるだけ。
本質的に違うので、扱いも全然ことなるケースが多い。
ロジック側でケアしないといけないことが増える
様々な処理で[if 種別id == 1 else ...]みたいなものを書かないといけなくなる。
また、おまけ機能追加時に既存処理にも影響を与える。
(いままで購入商品しか取得されない前提で書かれていた処理に、急におまけ商品のデータが入ってくることになる。)
でもこれは、データ取得部分がclassできれいに定義されているならclass内で完結させて、それほど問題にならないかもしれない。
次のヤツのほうが問題かも。
おまけ商品にだけ持たせたいデータが出てきたときに困る
(ちょっと良い例がおもいつかんけど)
「あるおまけ商品は不良品でも交換対応しないとか、するとかのフラグをもたせたい」みたいな要求が出たとする。
このフラグはおまけ商品にだけつければいいけど、テーブルが同じなので通常商品にもついてしまう。
購入詳細テーブル
id | 購入id | 商品id | 個数 | 種別id | 交換対応可フラグ |
---|---|---|---|---|---|
1 | 1 | 1 | 10 | 1 | true |
2 | 1 | 2 | 1 | 1 | true |
3 | 2 | 1 | 5 | 1 | true |
4 | 1 | 10 | 1 | 2 | false(商品id:10のおまけ商品は変更対応不可) |
5 | 2 | 11 | 1 | 2 | true(商品id:11のおまけ商品は変更対応可) |
通常商品は全て交換対応を行う前提なので基本全てtrueだけど、もしfalseが入っていたら処理はどうなるのだろうか?
「通常商品は全て交換対応を行う」という前提に沿って実装が行われていれば「交換対応処理」は動作しそうだし、データに沿うように実装されていれば「交換対応処理」は動作しなさそうである。
コードを追わないとどうなるのかよくわからない。
では「種別idで持たせたいデータが異なるなら、自由に項目を設定できるjsonカラムを用意しよう。」みたいな発想になったりする。
購入詳細テーブル
id | 購入id | 商品id | 個数 | 種別id | other_data |
---|---|---|---|---|---|
1 | 1 | 1 | 10 | 1 | null |
2 | 1 | 2 | 1 | 1 | null |
3 | 2 | 1 | 5 | 1 | null |
4 | 1 | 10 | 1 | 2 | {return: false} |
5 | 2 | 11 | 1 | 2 | {return: true} |
=> jwalkというアンチパターン。最悪。
ロジック側で「種別id = 1だったらjsonカラムにこういうデータが入ってるはずだから (<= この知識は実装者以外はどこから得られる?)こうやってデータを取る。」ってのがそこら中に発生して死ぬ。
テーブル定義を見ても何が入るのか、どうやって使われるのかわからず、稼働中のデータやロジックを追ってみないと定義が把握できない。
"return"以外に入ってくるデータがあるかもしれない。。等、何も信用できなくなる。
まとめ
上記のような、定義のわからないテーブルが増えるとメンテナンスがしんどくなる。
定義のわからないテーブルが1つあるより、使われ方が明確でシンプルなテーブルが10ある方がメンテナブルであると思っている。