コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
第2正規形
商品名を直すのに、注文明細を全部書き換える
注文明細の表を考えます。1 行が「どの注文の、どの商品が、いくつ」を表すので、行を特定するには注文 ID と商品 ID の 2 つが要ります。主キーが 2 列の組になっている表です。
ここに、表示が楽だからという理由で商品名と価格まで書き写しました。
| 注文 ID | 商品 ID | 個数 | 商品名 | 価格 |
|---|---|---|---|---|
| 1 | 100 | 2 | りんご | 500 |
| 1 | 200 | 3 | バナナ | 300 |
| 2 | 100 | 1 | りんご | 500 |
商品名を「りんご」から「青森りんご」に直す指示が来ました。直す先は商品の表ではなく、その商品が登場した過去の注文明細の全行です。3 年分あれば数万行になります。
しかも、数万行のうち 1 行でも書き換えに失敗すれば、同じ商品 ID の行に 2 つの名前が並びます。どちらが正しいのかは、もう表からは判断できません。
主キーの片方だけで決まる列を探す
なぜこうなったのかは、列を 1 つずつ見ていけば分かります。
個数は、注文 ID と商品 ID の両方が決まって初めて決まります。ところが商品名は、商品 ID だけで決まります。注文 ID は関係ありません。価格も同じです。主キーの一部だけで決まる列が、主キー全体で特定する表に混ざっている。これが直すべき箇所です。
この点検は、主キーが 2 列以上あるときだけ意味があります。主キーが 1 列なら「その一部」というものが存在しないので、条件は自動的に満たされます。意味のない番号を主キーに立てる設計が広まっているのは、こうした点検が要らなくなる効果もあるからです。裏を返せば、間に立てた表のように主キーを組にしたときだけ、列ごとに問い直す必要があります。
設計レビューでの手順も決まっています。まず主キーが何列あるかを数える。2 列以上なら、残りの列を 1 つずつ取り上げて「この列を決めるのに、主キーの両方が要るか」と問う。片方だけで足りる列が見つかったら、その片方と一緒に別の表へ出す。それだけです。
切り出し先は商品の表です。商品名と価格をそちらへ移すと、名前の変更は 1 行で終わり、まだ 1 度も注文されていない商品も登録できるようになります。
注文したときの値段は、商品マスタの値段ではない
ここで 1 つ注意があります。価格を商品の表へ移したのだから、明細から価格を消してよいかというと、そうではありません。
商品の表にある価格は「いまの価格」です。値上げをした瞬間に、過去の注文の合計金額まで変わってしまいます。売上の集計が昨日と今日で食い違い、発行済みの領収書とも合わなくなります。
そこで明細には、注文が成立した時点の単価を別に持たせます。これは書き写しに見えますが、切り出す対象ではありません。「いまの価格」と「あのとき合意した価格」は、たまたま値が同じだっただけの別の事実だからです。単価は注文 ID と商品 ID の組で決まるので、主キーの一部だけで決まる列にも当たりません。
見分け方は「その値がいつのものか」を問うことです。同じ 500 円でも、商品の表の 500 円は今日の値段、明細の 500 円は去年の 3 月に合意した値段です。名前が同じで値も同じでも、指しているものが違えば別の列として持ちます。
主キーが組になっている表で、その一部だけで決まる列が残っていない状態が第 2 正規形です。