かっちゃんのプログラミング奮闘記

このブログは、僕がプログラミング学習をしていく上で、知識のインプットを図るためのアウトプット場所として活用している場所です。

【書籍】達人に学ぶDB設計徹底指南書を読んで学んだこと【第3章】

📖第3章

テーブルについて

テーブルとは2次元表に整理された情報を格納できるもの。

普通の表との違いは、カラム(縦列)やレコード(横列)で一貫して情報が整理された状態で表に入るのが普通の表と違うところ。ただ単に表にたくさんの情報を入れれば良いだけではない。

テーブル内部には複数のたくさんの情報が格納されるので、テーブル名は複数形で命名する。

テーブル構成の要素

テーブルは「列(カラム)」と「行(レコード)」で構成されている。 列は2次元表の縦の列のことで、行は2次元表の横の1行のことを指す。

キー

目には見えないけど、DBのテーブルになくてはならないもの。キーの種類は主に2つある。

  1. 主キー
  2. 外部キー

以上の2つがキーに主に存在する。

主キーとは?

主キーはテーブル内に必ず存在しないといけないキーでして、1行のレコード上に必ず1つだけ主キーを作らないといけない。

テーブル内に1つだけ独立させたキーを作成しておくことで、内部情報が混同したりすることなく、任意の1行のレコード情報を取得することができる。

このことを一意に識別する「unique」という。 migrationファイルによく出てくるやつかな?

外部キー

外部キーは2つの複数のテーブル同士を制約を持して紐付けるものになる。

要は、親子関係(アソシエーション)を作る時に必要になってくる。

外部キーがあることで、複数のテーブルを関連付けた時に、「制約」を定義することができ、間違った情報をDB内で変更や削除してしまう恐れを防いでくれる。(よく言う外部キー制約というやつ)

💡ポイント

外部キー制約を設けて親子関係を作る時は、子のテーブルの内部を先に削除して、親のテーブル内部は後から必要に応じて削除する様にするべし。

そうじゃないと、親子関係の親側が削除された時に、子の親がいなくなるので、エラーが出てしまう。

また、外部キーは変更可能なカラム名で親子関係を結んではいけない。

なぜなら変更されてしまって重複したり、判別をRDMS側が間違えてしてしまう可能があるから。

(親側に「山田 太郎」という名前の間に空白をつくて保存して、子側に「山田太郎」と空白をなしにして保存してしまうと、同じ意味でもRDMS側では違う意味と解釈して紐付けれくれなくなるから注意。)

なので、外部キーで親子関係を作るときは、必ず「ID」の様な人為的ミスがない「固定された表記」で関連付けることが鉄則。(board_idとtag_idを紐づけるなど必ずIDで紐づける)

🔒制約(外部キー)

  1. NOT NULL制約
  2. 一意制約
  3. CHECK制約

外部キーに関して代表な制約が以上の3つあります。

NOT NULL制約

空白の状態でDBに保存できない様にする制約。
NOT NULL制約を入れることで、SQLでDB内に情報を追加したり更新したりする時に、エラーが出て保存できない様にすることができる。

基本的にDBに情報を保存する場合は、空白・空欄で保存できない様にすることが設計をする上で絶対条件なので、テーブル設計する上ではできる限りNOT NULL制約を入れる様にする。

一意制約

列(カラム)に一意性を設定できる制約。
主キーと同じ役割だが、主キーはテーブル1つに一意性を1つしか設定することができないのに対して、こちらは何個でも制約を設けることができる。

CHECK制約

ある列(カラム)の中で取り出したい範囲を指定して、その範囲だけしか取り出せない様に制限をかけること。

例えば年齢の列なら「20〜45までの整数」と範囲の制限を設けることができる。

正規化とは何か?

論理設計事に使われる概念で、正規化を行うことで正規形を定義することができる。

正規形とは?

論理設計事の情報を整理して綺麗にDBを作り上げるために必要な概念。

正規形を行うことで、1つの情報を複数のテーブルに間違えて作成してしまうとかみたいな余分な冗長なことを未然に防いでくれて、開発を効率化してくれる。

正規形は第1正規形〜第5正規形まであり、実際の業務では第1正規形→第3正規形まで考えてつく割れることが多いので、まずは第3正規形まで理解できる様になっておくと良い。

第1正規形

第1正規形の定義は、「1つのセルの中に1つの値しか入っていない状態」のことを指す。

セルはフィールドとも言う。 セルはレコード(行)の中の1つのマスの中に入っている情報のこと。

第1正規化をする際は、このセルの中には1つしか情報、値は1つしか入れてはいけない。

ちなみにこの1つのセルの中に1つだけの値が入っていた場合、その値は「スカラ値」と言われる。

もし1つのセルの中に複数の情報を入れてしまうと、主キーが各列の値を一意にできず、正確に情報を取得することができなくなってしまうから。

関数従属性

第1正規形は、関数従属性と言う概念を採用している。 関数従属生徒は、「Y=10ならばX=5」である。「Y=8なら、X=3」である。みたいに、どちらか片方が決まれば自ずと紐づいているもう片方の情報も特定することができると言うこと。

◇例

「社員ID: X ならば → 社員名: Y 」
「社員ID: 100 ならば → 社員名: 徳川」
の様にX列の値を決めれば、Y列の値が1つだけに絞れて決まると言うこと。

・社員名: 徳川 ならば → 子供: たつお
・社員名: 徳川 ならば → 子供: みっちゃん
と言う主キーでの一意性を認められない形は設計としてダメなので、きちんと正規化して複数のテーブルと紐付ける。
(主キーは物語の主人公の様なもの、主となる基準とする貴キーがいないと始まらない)

第2正規形 〜部分関数従属〜

第2正規形では、部分関数従属という1つの行に複数の主キーで管理している状態を解消し、主キーを構成する行(レコード)の全ての列(カラム)に1つの主キーでの従属(紐付け)させる様にする。

この行の中の1つの主キーで、その行に関わる列を全て管理する状態にすることと「完全関数従属」と言う。

💡ポイント

第2正規形では、部分関数従属を解消して→完全関数従属に変換することを行う。

✅第2正規化の方法

第2正規化する方法は簡単で、1つの行に主キーを1つだけにすれば良いだけ。
要は、テーブルを2つないし、複数に分けて作成し、複数の主キーが1行に集まっていたのを分離すれば解消できる。

例えば、社員テーブルの「会社コード」のカラムだけに従属する「会社名」と言うカラムが1つあり、さらに「会社コード」のカラムの「社員ID」のカラムに従属する「社員名」と言うカラムが1つあった場合、複数の主キーが1行のレコードの複数従属してしまっており、部分関数従属の関係が出来上がってしまっていた場合、

「会社コード」のカラムに属する「会社名」のカラムと言う関係性の表を社員テーブルから独立させてあげれば良い。

その結果、「社員テーブル」が1つだけだったのから→「社員テーブル」と「会社テーブル」の2つのテーブルが出来上がる。

恐らくのちにこの2つの分けたテーブルを親子関係でアソシエーションで結んで使ったりするのだろう。

第3正規形 ~推移的関数従属~

表の中に2段階の関数従属がある場合、それを推移的関数従属と言い、さらに正規化する必要がある。

[会社コード,社員ID]→[部署コードID]→[部署名]
以上の様な部署名にたどり着くまでに2段階になっており、もしかりに社員IDに従属する社員名が空白(NULL)だった場合、下の階層の部署名は社員名がNULLになってしまっていて、一緒にNULLのままRDBに保存することができない。

つまり今回の場合でも、部署に関連づく社員がいない限り、RDBには新規の部署名を登録することができない。

なのでこの様なことが起こりうるので、部署コードIDも独立させて、複数のテーブルに分けてアソシエーションにして独立させる。

今回の場合は、社員テーブルから部署コードを独立させて、部署テーブルを作成する。

その後はその部署テーブルの部署コードを社員テーブルの部署コードとIDでアソシエーションで結びつけて1対1だったり、多対多の関係などを結ばせる。

💡ポイント

原則として第3正規化までは必ず行う。
ただし、正規化してテーブル数を増やしすぎるとSQL文で結合させることが多くなり、結果パフォーマンスが悪化してしまうことにつながるので、そこら辺は気をつけること。