.ログイン
ユーザ名:

パスワード:


パスワード紛失

新規登録

.検索

.メインメニュー

.フォーラムメニュー

.オンライン状況
15 人のユーザが現在オンラインです。 (14 人のユーザが フォーラム を参照しています。)

登録ユーザ: 0
ゲスト: 15

もっと...

.
.リンク集

メイン
   CakePHP モデル(Model)
     HABTMの効率
投稿するにはまず登録を

スレッド表示 | 新しいものから 前のトピック | 次のトピック | 下へ
投稿者 スレッド
siroiruKa
投稿日時: 2009-6-13 12:37
Baker スタート
登録日: 2009-6-11
居住地:
投稿: 14
HABTMの効率
PHP及びデータベースをさわり始めて3週間の素人です。
(ツッコミ所満載の文章になっていたらすいません)

現在CAKEでデータベースサイトを作っています。
CAKEに詳しい方々が、HABTMについてどのように考えているのか参考までにお聞かせ願えれば、と思ってスレッドを立てました。


例えば、

『users』というテーブルがあり、『fruits』というテーブルがあるとします。
『users』にはユーザーの情報が格納され、『fruits』にはidと共に果物の名前が格納されます。

---fruitsテーブルの例----
id:1 name:バナナ
id:2 name:リンゴ
-------------------------

さて、ここで『あるユーザー』が好きな果物一覧を取得したい。
あるいは『ある果物』が好きなユーザー一覧を取得したい。となった場合。

いちばん簡単に実現できるのは、
joinテーブル『fruits_users』を作ってHABTMで関連付けることのように思います。

しかし、一つだけ心にひっかかることがあります。
それは、HABTMを使うとデータベース自体の容量がむやみに膨らんでしまうのではないか? 
という疑問です。

ユーザー100人、果物100個というデータベースであれば、データは200個で済みます。
しかしjoinテーブルを作って、あり得る『fruits_users』の組み合わせが
全て結ばれてしまった場合は、100×100=10,000個のjoinデータができてしまいます。

それは、なんだか効率的におかしいな、と思うと同時に、
もしかして自分がHABTMの概念を誤ってとらえているのかな? とも思ってしまいました。

全ての組み合わせが結ばれる、という可能性は低いと思うのですが、
ユーザーが10,000人、果物(別に果物じゃなくていいんですが…)が100万個とかあったら、
joinテーブルが凄まじく大きなテーブルになりそうな気がしてしまいます。

CAKEユーザーの皆様はそういうことを気にしたりするのでしょうか?
少し、気になりました。
     
MASA-P
投稿日時: 2009-6-13 14:22
Cake 職人
登録日: 2008-1-24
居住地:
投稿: 224
Re: HABTMの効率
HABTMについて、というよりは、データを扱う上での理論的な部分を悩まれているのではないかと思いますが、たぶん難しく考えすぎているのではないかと思われますので、整理してみましょう。

HABTMというのは、簡単に言えばAとBのデータを「多対多」で結ぶためのものです。このほかに、「一対多」という場合と「一対一」があり、前者がhasManyもしくはbelongsTo、後者がhasOneです。

さて、一般的にデータの結合には、データを結びつけるための情報が不可欠になります。それが、接続する相手方の固有の値であり、CakePHPのモデルではidをデフォルトで使用します。例えば、usersとfruitsがあるのなら、もしuserを複数のfruitと結びつけたいのならfruitの中にuser_idが、反対にfruitに複数のユーザを結びつけたいのなら、userの中にfruit_idが必要です。相手方のidがテーブル中に存在していないと、相手先が分からないので結合できないのは分かりますよね。

ちなみに、「hasMany」と「belongsTo」の違いが結構分かりにくいのではないかと思うのですが、マニュアルにはそれぞれ「複数持つ」「属している」と書かれていますが、実は「どちらに相方のidが存在しているか」の違いだけで、実は構造的には同じものを意味しています。例えば、fruitの中にuser_idが含まれている構造なのであれば、user側から見れば「hasMany」であり、fruit側から見れば「belongsTo」です。

ここまでがまあ予備知識的なものですが、更に進んで、user側から見てもfruit側から見ても「複数ある」という状態をどうやって作り出すか、という点を考えてみましょう。

まず思いつくのが、「userにもfruitにも相方のidを置けばいいのではないか」ということです。こうすれば、User hasMany FruitでありFruit hasManyであり、User belongsTo Fruitであり、Fruit belongsToである関係が作れます。つまり多対多になります。
しかし、この構造には重大な間違いがあります。それは「それぞれ独立した事象を表してしまう(関連性が無い)」事が起きてしまうのです。

たとえばこんなデータを考えてみます。

・Aさんはリンゴとバナナが好き
・Bさんはブドウとナシが好き

「相手のidを入れる場所」は、先にフックのついたコードリールのようなものだと考えてみてください。相手のidが存在する、ということは「相手にフックを引っかけた」状態です。相手のデータには、複数のフックを引っかけられますが、コードリールは1つです。
この場合ですと、フルーツ側にコードリールがあり、リンゴとバナナのフックにはAを、ブドウとナシのフックにはBを引っかけます。

では、今度はユーザ側のコードリールからフルーツにフックを引っかけてみます。

・リンゴはBが好き
・バナナはAが好き
・ブドウは???
・ナシは???

上ではAさんがリンゴが好きなはずなのに、Bに関連づけられてしまいました。
また、ブドウとナシは関連性が現せなくなってしまいました。
これではまずいですよね。

じゃあ、どうしたらいいのでしょう?
両端にフックのついたコードリールなら、それぞれのデータに複数のフックが引っかけられますよね。
そこでHABTMの出番なのです。
fruit_usersというテーブルは何なのかというと、この「両端にフックのついているコードリール」なのです(フック付きコードリールが2つついている箱、という感じですかね)。
逆に、これ以外の方法だと、重複してデータにフックがかけられません(考えてみてください)。

なので、siroiruKaさんが心配している「100×100=10000になる」というのは仕方のないことで、これ以外に表現はありません。でも、これはあくまでもMAXであり、実際には全員が100個のフルーツが好き、ということではありませんよね。例えば各人平均3つのフルーツと関連しているのなら、300のfruit_userが存在することになります。

蛇足になりますが、「3個までのフルーツをもつユーザ」とかは出来ると思います。userの中に、コードリールを3つに増設すればいいのです。この場合は同じテーブルを参照する3つのモデル(Fruit1, Fruit2, Fruit3)を生成し、アソシエーション内の「相手のid」の部分を固有の名前(fruit1_id, fruit2_id, fruit3_id)等にすれば実現可能かと思います。しかし、それぞれ独立した「値(相手のid)」が必要になる点と、不確定な個数は扱えないため、いくつになるか分からないものを扱う場合は、どうしてもHABTMが必要です。
siroiruKa
投稿日時: 2009-6-14 2:07
Baker スタート
登録日: 2009-6-11
居住地:
投稿: 14
Re: HABTMの効率
非常に分かりやすい解説、ありがとうございます!
半日かかりましたが、HABTMでデータを引きずってくることができました。

データベースの仕組みを理解するキッカケを頂いたように感じます。
特に、関係性をデータベース化するという発想が自分の頭では受け入れにくいものだったので助かりました。
(joinテーブル、という存在が非常に面白いものだと思いました)

「hasMany」と「belongsTo」についての簡潔な説明も頂けて、モヤモヤが取れました。

データの流れを制御しきれるかどうかちょっと心配ですが、頑張りたいと思います。

スレッド表示 | 新しいものから 前のトピック | 次のトピック | トップ

投稿するにはまず登録を
 


. .