質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

データベース設計

データベース設計はデータベースの論理的や物理的な部分を特定する工程です。

Q&A

解決済

2回答

1758閲覧

注文詳細テーブルの外部キーである商品テーブルが更新された場合、購入当時の商品情報はどこに保存するのか

zendendo

総合スコア43

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

データベース設計

データベース設計はデータベースの論理的や物理的な部分を特定する工程です。

1グッド

2クリップ

投稿2018/06/17 08:50

編集2018/06/17 09:09

###実現したいこと・前提
railsでmysqlをつかっています。
通販のデータベース設計をしており、
過去の注文を見れるようにしたいと考えています。

よく通販のデータベース設計の例として
買い物の注文を確定させたとき、
注文テーブルと注文明細にレコードが作成され、
注文明細テーブルには、商品の情報が必要なので、商品テーブルを参照する外部キーが入っていると説明されています。

注文テーブル(注文詳細を多数保有している親テーブル)
|id|注文日時|顧客(FK)|小計|手数料|支払合計額|
|:--|:--:|--:|

注文詳細テーブル(一つの注文に従う子テーブル)
|id|注文id(FK)|商品id(FK)|購入数量|単価|
|:--|:--:|--:|

商品テーブル(多数の注文詳細から参照される親テーブル)
|id|商品名|商品説明|価格|
|:--|:--:|--:|

###疑問点
ここでふと疑問に思ったのですが、
商品の更新(例えば、価格や説明文とかが変更)されたり、
販売中止とかで削除されてしまうと困ったことになるのではないかと。

例えば、
外部キー制約(RESTRICT)で、親テーブル(商品)の変更・削除を許さない場合は、
商品情報を更新できなくて困るかと思います。
かといって外部キー制約CASCADEにすると新しく更新された商品情報が子テーブルである注文詳細にも反映されてしまうため、購入した当時の商品情報を見ることができなくなってしまいます。
外部キー制約SET NULLにした場合も、購入当時の商品情報が見れなくなります。(また、頻繁に商品が更新されるのならば、注文明細の外部キーカラムがnullだらけで参照する意味が薄くなるのではないか?)

この場合、
購入当時のアイテム情報を見れるようにするにはどのテーブルに保存すればいいのでしょうか?
(新しいテーブルをつくるのか、または注文詳細テーブルに商品名、商品説明などをカラムを新たにつくって記録すればいいのでしょうか?)
教えて頂ければ幸いです。

xenbeat👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

xenbeat

2018/06/17 09:08

とりあえずタグに「データベース設計」を追加しましょう。
zendendo

2018/06/17 09:09

追加します!
xenbeat

2018/06/17 09:11 編集

「注文明細テーブルには、商品の情報が必要なので、商品テーブルを参照する外部キーが入っていると説明されています」→ 誰が説明しているのですか?どこかでそのような記事を見たのか、誰かに質問したのか等、具体的に追記ください。
zendendo

2018/06/17 09:27

特定の誰かというわけではないのですが、ネットでdb設計をいくつか調べた際に、注文詳細に商品idを含めているものをいくつか見かけたのでそう書かせていただきました。参考サイトの内のひとつhttp://www.atmarkit.co.jp/ait/articles/0605/11/news124.html
guest

回答2

0

ベストアンサー

購入当時のアイテム情報を見れるようにするにはどのテーブルに保存すればいいのでしょうか?

(新しいテーブルをつくるのか、または注文詳細テーブルに商品名、商品説明などをカラムを新たにつくって記録すればいいのでしょうか?)

結論から言うと、後者の注文詳細テーブルに商品名、商品説明などをカラムを新たにつくって記録が良いでしょう。

データベース設計を学ぶと、できるだけ正規化しましょうという考え方もありますが、
注文関連のテーブルみたいに、取引を記録するようなテーブルはあえて非正規化というアプローチを取ります。

なぜ非正規化するのか

注文関連のテーブルは、いつ、誰に、何を、何個売り、合計はいくらで、いつ何で支払いされて、いつ何で納品するか等、
取引情報を時系列に記録するログファイルだと思ってください。

これらのテーブル(ログファイル)に挿入(記録)されたデータ(ログ)はイミュータブルであり、
そのデータを更新するということは、極端な言い方をすると取引データの改ざんとなります。

なので、注文された商品情報は、非正規化した注文関連のテーブルに、注文時点の情報をそのまま保持するのが良いという考えになります。

補足

少し言葉足らずだったようなので補足。
既にご認識かと思いますが、ある商品が過去に何度か注文された後に、その商品の価格が変わったり、その商品が廃番になったりと、商品と注文のライフサイクルは異なります。
このように、ライフサイクルの異なるエンティティ同士はリレーションを張らないというのが設計的にはベストと言えるでしょう。
ただ、使用しているアプリケーションフレームワークで、リレーションを張らないといけない等の制約がありましたら、この限りではありません。

投稿2018/06/17 09:45

編集2018/06/19 02:49
xenbeat

総合スコア4258

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zendendo

2018/06/17 11:58

maverixzさん、ありがとうございます。 非正規の方がシンプルで理解しやすいです!今後は変更したくない履歴系の記録に入る外部キー(外から引っ張ってくる情報)には非正規化していきたいと思います。
sazi

2018/06/18 02:17

それを非正規化と言うかは微妙ですが、外部キー制約は付けないことは忘れずに。
xenbeat

2018/06/19 01:42

saziさん補足ありがとうございます!
sazi

2018/06/19 02:22

追記部分についてコメントします。 「非正規化=リレーションしない」ではないので、今回のトランザクション側に実績を残すというのは、「ボイス・コッド正規形」にまでは至らないので外部キー制約を外すという事だと思います。 例えば、商品の実績を集計する場合には、取引の実績に商品コードが必要で、それは外部キーとしてリレーションの関係にはあります。
xenbeat

2018/06/19 02:53 編集

> 「非正規化=リレーションしない」ではない 確かにこれは確かに語弊が生まれるので修正します。 > 例えば、商品の実績を集計する場合には、取引の実績に商品コードが必要で、それは外部キーとしてリレーションの関係にはあります。 将来的に商品コードだって変わる可能性がありますので、取引の実績に商品コードを保持するのはもちろん同意で、ここにリレーションがあるかというところに認識齟齬があると思います。 「コード」を保持するのでリレーションはあると言えばあると思うのですが、外部キー制約は付けない(ライフサイクルが異なるのでその時点の商品情報は注文に全て保持する)方が良いという考えです。
sazi

2018/06/19 02:57

maverixzさんとの、認識は合っていると思っています。 質問者さんが「(このようなケースは)非正規化していきたいと思います」というコメントを残されたのが気になりましたので、表現的なところだけです。
xenbeat

2018/06/19 02:59

なるほどそういうことですね。 諸々フォローありがとうございます!
zendendo

2018/06/20 01:20 編集

捕捉ありがとうございます。 自分の用語の使い方と解釈が間違っていたかもしれないので、改めて平たくおさらいすると ・注文詳細テーブルに当時の情報を保存したい場合は外部キー制約は使わず、単なる商品コードとして記録する。 ・注文詳細には、当時の商品情報を保存するため、商品名、商品説明、価格を追加して記録する。 ということですね! カラム追加後の注文詳細テーブル id,注文id(FK),商品id,商品名、商品説明、価格、購入数量、単価
guest

0

カラムにその商品情報の開始時期・終了時期を作成する。

登録時にUPDATEをかけるのではなくINSERTをする。

時期によって表示を変えるプログラムを書く。

これらで出来ると思います。

回答になっていますか?

投稿2018/06/17 09:04

van-0215

総合スコア89

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zendendo

2018/06/17 11:52

van-0215さん、回答ありがとうございます。商品を更新替わりに作成していき、その中で最も新しいレコードだけを注文詳細テーブルの外部キーフィールドに入れるということでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問