突然ですが、今後のネタの前提として、正規化の方法と、正規化を崩す方法について、まとめてみたいと思います。
■■ 正規化とは
正規化は、そもそも、データベースの目的である
一貫性
共用性
独立性
を保つため、同一の情報を、複数のテーブルに持つことをさけるために行われる操作です。
(もし、複数テーブルに持ってしまうと、それはそもそもデータを共用していないし、どっちかのテーブルだけを更新して、どっちかのテーブルを更新し忘れるという一貫性に反する問題も出てくる)
で、この正規化には、第一正規形から第五正規形までありますが、第4、第5正規形に関しては、情報が欠落することもあったり(気がする。。自信ないな)するので、一般的には行いません。
つまり、第三正規形(現実的には、さらに、「すべての列が主キーに完全関数従属で、他に完全関数従属関係がない」ボイスコッド正規形)まで、落とし込むことが、「正規化した状態」となることになります。
なお、正規化の目的に、データスピードの速さを上げる人もいますが、(検索スピードは有利になることがありますが)更新も含めた、トータルのレスポンスタイムは、正規化したから早くなるとは、言い切れません(つまり、検索スピード向上と、更新スピード劣化のトレードオフになる関係のものが存在する。ただ、正規化したほうが改善する場合が、「多い傾向にある」ことは言えると思う)
■■ 正規化の手順
で、手順なのですが、これは、散々本に書かれているので、省略といいたいところなのですが、あとあと問題になるので、ちょっちつけたし。
で、一般的な正規化手法の説明は、ここを見てください。
(上記の斜体、ボイスコッド正規形の説明は、そこからとってます)
で、簡単に言っちゃうと
(1)第一正規形:繰り返しを排除する。
繰り返しの部分を、別テーブルにわけ、別テーブルのように、もともとの親テーブルのIDをもつ。
例:受注伝票に、品名・数量・単価の繰り返しがあったら、
そこを分けて、
受注IDというのが親のテーブルに(第二正規形で)つくから、
それを入れて、受注ID,品名・数量・単価をもつ、受注明細テーブルを作る。
なお、受注明細テーブルには、第二正規形で、受注明細IDがつく)
(2)第二正規形:主キーをきめて、そのテーブルに属する項目を集める
この、集めるとき、その項目値は主キーがきまったら、必ず、この値になる(これを関係従属という)にならないといけない。
たとえば、gooログイン名のばあい、xmldtpなら、ウィリアムのいたずらである。
(3)第三正規形:第二正規形になったテーブルの中に対して、この値が決まったら、この値は、かならずこうなる!というものがあったら(これを関係従属という)をテーブルを分ける。
■■ 正規化をくずす理由
正規化を崩すのは、現場的には、主に2つの目的で行われる。
(1)トータルのレスポンスタイムを減らすとき
→現在では、正規化を崩しても、かならずしも、これは、減らせない(理由は、次回以降で述べる)。なので、昔は行われたが、最近は、どーなんだろー。
(2)正規化をすると、将来的に不利になり、仕様変更のときに困ると思われるとき
これは、Web系のアンケート調査などでよくあることなのだが、
アンケート用紙に、アンケート結果、氏名、住所、勤務先、勤務先住所を書くような場合、
正規化にしたがえば、
アンケートテーブル:アンケート結果、回答者ID
回答者テーブル:回答者ID、住所、氏名、勤務先ID
勤務先(会社)テーブル:勤務先ID,勤務先、勤務先名
となる。しかし、アンケートテーブル1つだけを作成して、更新する場合も多い。
なぜなら、このアンケートであつめた、回答者、勤務先が、将来、利用されることがまったくなければ、ここで、一貫性も、独立性も、共用性もなにもない。(このプログラムしかないのだから)。
さらに、ほかのアンケートがあったとき、このアンケートの回答者データを利用できるかどうか分からない(勤務先が違った場合、どちらかが間違えているのか、それとも2人とも正解で、表記がちがうのか、それとも、移転したのか、情報的に分からない)
つまり、このデータは、氏名、住所、勤務先、勤務先住所、とかくのは、まちがいで、本当は
このアンケートにおける氏名、
このアンケートにおける住所、
このアンケートにおける勤務先、
このアンケートにおける勤務先住所
が正しい解釈かもしれないという「可能性」がある。
この場合、上記正規化は間違いで、「このアンケートにおける」だから、氏名、住所、勤務先、勤務先住所は、アンケートテーブルに入れるのが正しい。
ところが、設計時点では、どっちになるのか、分からない場合がある(このアンケートが、1回きりのアンケートなら「このアンケートにおける」、会員制のものならば、正規化する形と分かりやすいのだが、解答用紙だけきて、作って!ということもあるので)。
アンケートの例などでは、まだまだ、やさしいが、受注テーブルにおける、品目名などが、それにあたる。(品目名を、直すケースがありえる)
それによる、正規化を崩す話題は、ここにある。
で、このようなケースで、はじめに正規化してしまい、あとで、「いやちがう、後者のように、これは、アンケートに従属させないといけない!」といわれると、プログラム的に結構な修正になる(更新時に、その部分を入れないといけないので)。
一方、逆に、回答者テーブルと、勤務先テーブルに分けるほうは、select文で簡単にテーブルは、できる。
なので、こういう仕様変更が入ることを見込んで、正規化をとめておくというケースが、Accessでの開発や、Webのアンケート調査などでは、結構あると思う。
このことについて、昔、このブログで、「Accessでアジャイル」なんとかいうので、かかなかったっけ?正規化をすぐにする必要はないって。。そのこと。。
なお、上記の非正規化で、みんな無意識のうちにやっているものとして、受注明細の単価がある。
単価は、商品マスターに普通入っているが、受注明細テーブルにも入れていると思う。
本当に、商品ー単価の関係であれば、商品が決まれば、単価も決まるので、商品マスタに入れないと、正規化を崩したことになる。しかし、受注明細に単価を入れても、「正規化を崩している」と誰も言わないし、実際崩していない。
この理由は、単価は、別に計算しやすくするためでなく、
売買が、10月2日におこり、単価50円
その後、10月4日におこり、単価60円に改定
伝票を、10月5日に作成
ときは、伝票にでる金額は、50円にしなければいけない。
つまり、商品単価と受注明細に現れる単価は、まったく別物で、本来、受注明細の単価は、「売却時単価」、商品マスタの単価は、「時価単価」というべきところを、めんどーくさいから、両方とも、便宜上単価といっているだけの話だからである(つまり、本来別物なのだが、たまたま名前と金額が同じなだけなので、混乱してる)
■■ 正規化の崩し方
理論上は、3つの崩し方があることになる
(1)第一正規形を崩す
(2)第二正規形を崩す
(3)第三正規形を崩す
ここで、「(1)第一正規形を崩す」ということは、ふつうしない。
Select文を書くのが大変だし、繰り返しが増えたら、テーブルを修正しないといけないので。。
まあ、ありえないんじゃないかなあ。。。
(2)そうではなく、普通は、第二正規形、第三正規形をくずす。
ひとつの例は、上記のアンケートのような場合、
アンケートテーブル:アンケート結果、回答者ID
回答者テーブル:回答者ID、住所、氏名、勤務先ID
勤務先(会社)テーブル:勤務先ID,勤務先、勤務先名
とすべきところを、
アンケートテーブル:アンケート結果、氏名、住所、勤務先、勤務先住所
として、追加を簡単にしてしまうケース。
もうひとつは、検索を簡単にする方法で、
受注マスタに
受注ID,受注先ID、受注年月日、
などがあり、
受注明細に
受注ID,受注明細ID,商品ID,商品数量、商品単価
とあるときに、受注明細に、受注マスタのデータをいれてしまう、
受注先ID、受注明細ID,受注年月日、商品ID,商品数量、商品単価
方法。
こうすれば、
●ある日、ある商品の受注といったとき、
<<正規化してあるほう>>
受注テーブルと受注明細テーブルをJOINして、そこから受注年月日と商品IDの条件をみる
とやるべきところを
<<正規化してないほう>>
受注明細テーブルから、受注年月日と商品IDの条件をみる
というので、JOINの回数がへる。JOINに時間がかかるDBでは、有効な手法だった(昔はJOINに死ぬほど時間がかかった)。
で、もし、覚えていたら、この先の話である、正規化したほうが早いという根拠、正規化を崩したほうが早いという根拠について述べてみたいと思います。
正規化したほうが早いという人は、正規化を崩したほうが早いという根拠の人たちの論拠に対して、切り崩す反論をしていない気がします(検索スピードの議論しかしていない。崩す人の論拠は、スループットの改善)。
ただ、これでも、正規化したほうが早いのですが、その早い理由である、行移動の件などについて、正規化を主張する人たちは書いてないし。。っていうことで、その辺の話題です。