状況
2019/01/01にスーパーボールを100円で「たかし」に販売した。
初期テーブル設計
商品マスタ
id | 商品名 | 価格 |
---|---|---|
1 | スーパーボール | 100 |
ユーザーマスタ
id | 名前 |
---|---|
10 | たかし |
売上詳細
id | ユーザーid | 商品id | 販売価格 | 販売日 |
---|---|---|---|---|
100 | 10 | 1 | 100 | 2019-01-01 |
正規化したテーブル設計
ここで、「売上詳細の販売価格カラムって、商品マスタが価格持っているんだから冗長じゃね?」 と思って正規化した。
商品マスタ
id | 商品名 | 価格 |
---|---|---|
1 | スーパーボール | 100 |
ユーザーマスタ
id | 名前 |
---|---|
10 | たかし |
売上詳細(販売価格は商品マスタから引っ張れるから消した)
id | ユーザーid | 商品id | 販売日 |
---|---|---|---|
100 | 10 | 1 | 2019-01-01 |
価格変更が発生
その後2019/05/01からスーパーボールの値段を50円に下げることになったので商品マスターの価格を変更したら、2019/01/01に「たかし」にスーパーボールを50円で売ったことになってしまった。。
商品マスタ(価格を50円に変更)
id | 商品名 | 価格 |
---|---|---|
1 | スーパーボール | 50 |
ユーザーマスタ
id | 名前 |
---|---|
10 | たかし |
売上詳細(価格を商品マスタから引っ張ったら50円で売った事になってしまった。。)
id | ユーザーid | 商品id | 販売日 |
---|---|---|---|
100 | 10 | 1 | 2019-01-01 |
どういうテーブル設計にしておくべきか?(個人的考え)
価格の変更履歴を持っておくようにする。
商品マスタ
id | 商品名 |
---|---|
1 | スーパーボール |
商品価格
商品id | 価格変更日 | 価格 |
---|---|---|
1 | 1982-01-01 | 100 |
1 | 2019-05-01 | 50 |
こうしておけば、2019/01/01の時点での価格が取得できるので上記の問題は起こらない。
※ただしこの場合販売した価格はロジックによる導出になるため、何らかのミスにより誤った値が導出されてしまう可能性もある気がする。やはり実際に販売した価格をデータとして持たせておくのは正しい気がした。
補足)マスターテーブルデータの書き換えについて
マスターテーブルの内容を書き換えるシーンは割と多い気がする。 商品価格が変わったり、商品名がかわったり。
その時にマスターテーブルの値を実際に書き換えてしまうと、過去販売データの集計などで正しく集計することができなくなる。
DBデータの変更はなるべく実際に変更するのではなく、履歴ログとしてレコードの追加で対応するのが良いのではないかと思っている。データの書き換えはデータ不整合を引き起こす可能性が高い。
データ取得がややこしくなるが、データ取得用のクラスをきっちり用意すれば、アプリロジック側からはややこしい事情を意識することなくデータ取得できるようになるはず。