INODEVLOG

WEBアプリ開発・事業開発・ビジネスモデル・読書の情報などをお届けしています。

RailsのDBテーブル設計で一対多か多対多かを判断するコツ

プログラミング初心者の方からよく、「データベースのテーブルが、この場合一対多にすべきか多対多にすべきか分からない」といった質問をされます。

慣れた方ならすぐに分かるかと思いますが、色々な情報が邪魔をして混乱してしまうようです。

そこで今回は自分が実際に説明する時に使っている、すぐにDBのテーブルの関係を判別するコツをご紹介します。

ぜひ混乱した際には活用してみて下さい。

一対多と多対多の絵を描いて考えよう

まず迷ったら視覚化して順番に考えていきましょう。

関係判別のやり方

例として「社員テーブル」「役職テーブル」という関係で見てみます。

ちなみに下記のような内容だとしておきます。

  • 社員は「名前」や「社員番号」などを持っている
  • 役職は「役職名」や「権限」などを持っている

Step1. ペンと紙を用意する

何でもいいので書けるものを用意しましょう。

もちろんペイントツールでもOK!

Step2. テーブル名と丸を書く

こんな感じに並べて書きます。

f:id:inodev:20171003062553j:plain

Step3. 横線とクロスさせる線を引く

この線が関係を表します。

f:id:inodev:20171003062647j:plain

Step4. テーブル毎に自分の関係を「多」か「一」か考える

次の2つの質問に答えて関係を判別していきます。

  • 「2つの(Aテーブル)」が同時に「1つの(Bテーブル)」を持つことがあるか?
  • 「1つの(Aテーブル)」が同時に「2つの(Bテーブル)」を持つことがあるか?

具体的に見てみましょう。

1つめの質問は『「2人の社員」が同時に「1つの役職」を持つことがあるか?』です。

今回社内に部長が2人いるという状態を許すのであればこれはYesですね。

2:1で関係が成り立っているので、少なくとも社員テーブルの方は多の関係になります。

f:id:inodev:20171003062805j:plain

次に『「1人の社員」が同時に「2つの役職」を持つことがあるか?』です。

ピラミッド構造の昇進のシステムで成り立つ会社であればNoですね。

1人に1つしか役職が無いのであれば、社員テーブルの方は2:2ではおかしくなるので役職テーブルの方は1です。

つまりこの場合は多対一の関係となります。

f:id:inodev:20171003062826j:plain

ただし企業によっては「役職を兼任する」という運用が存在することもあるでしょう。

その場合は、『「1人の社員」が同時に「2つの役職」を持つことがある』ので、2:2でもなりたつ、つまり多対多の関係になります。

f:id:inodev:20171003062923j:plain

混乱した時には全体をいっきにつかもうとせずに、片方(1つのレコード)から見た場合に相手はどういう関係になりえるかと順番に見て行くと判断しやすいです。

上記の名前などのように具体的に持っているデータを書くとより分かりやすいかもしれません。

一対多と多対多のプログラミングの挙動から考える

普通はデータありきでアプリの設計を考えるかと思いますが、RailsのMVCなどのソフトウェアアーキテクチャの方から入った人は、モデル構造を意識して『コード上でどう使いのか?』と先に処理として欲しい挙動から考えても分かりやすいです。

実際やってることは「絵を描いて考える」と全く同じですが、前後の処理が絡む場合にはこちらの方が分かりやすい場合もあるかもしれません。

Railsのソースでの関係判別のやり方

(1)社員と役職のモデルが下記のように存在するとする

class Employee < ApplicationRecord
  (何らかのリレーション)
end
class Position < ApplicationRecord
  (何らかのリレーション)
end

(2)ControllerやViewなどで「どのように情報を取り出したいか」具体的な処理で考える。

position = Position.find(1)

上記のように取り出したPositionに

#この様に複数の相手を取得する可能性があれば相手が多
position.employees 

#この様に一意に確実に特定できるのであれば相手が1
position.employee   

同様に相手Modelのインスタンスから見て、

employee = Employee.find(1)
#この様に複数の相手を取得する可能性があれば相手が多
employee.positions

#この様に一意に確実に特定できるのであれば相手が1
employee.position

というように判別できる。

両方複数相手が取り出せるという想定なら多対多で、片方が一意に特定出来るものなら一対多(または多対一)となります。

まとめ

取り敢えず混乱したら紙にテーブル名と丸、及び線を書いてひとつづつ関係を検証していきましょう。

混乱せずにDB設計出来るように使ってもらえたら幸いです!