自己結合(N対N関連付け)って何。

名前が強そうだと思ったので、学習してみました。

自己結合とは?

データモデルを設計していると、時に自分自身に関連付けられる必要のあるモデルに出会うことがあります。たとえば、1つのデータベースモデルに全従業員を格納しておきたいが、マネージャーと部下(subordinate)の関係も追えるようにしておきたい場合が考えられます。この状況は、自己結合関連付けを用いてモデル化できます。

(https://railsguides.jp/association_basics.html#自己結合)

N:Nの関連付けをする際に用いられている表現。

同一テーブル内の項目にそれぞれidを振り、関連付けを行うこと。

自己結合、すなわち、「自己(自分のテーブル内)結合(紐付け)」と認識している。

Railsチュートリアルで、FollowとUnfollowの関係を表現する際に使っていた。

 

例えば、北海道うまいもん展が開催されているとする。

また、北海道各地のうまいもんが露店形式で立ち並んでいるとする。

そして、各地の露店では、厳密には〇〇産ビール、〇〇産うにのように違うかもしれないが、抽象度を上げるとビール、うに、ラーメンという商品を売っている。

 

・全体像

北海道うまいもの展(HokkaidoUmainon)で各地の露店が立ち並んでいる。

・Table構成

ItemsTable: 札幌セット、函館セット、知床セット、 ラーメン、ビール、タラバガニ、うに、ソフトクリーム、ドーナッツ、味噌汁、鮭、熊肉

RelationhsipsTable: food_id, set_menu_id

 

北海道うまいもん展で売られているものは以下の通り

menu,price

札幌セット    1,700
知床セット    1,200
網走セット    1,300
ラーメン    800
ビール    600
タラバガニ    400
うに    1000
ソフトクリーム    300
ドーナッツ    200

 

自己結合の考え方を使って、それぞれのセットと内容を紐づけていく。

札幌セット: ラーメン・ビール・ソフトクリーム

知床セット: ビール・タラバガニ丼・ドーナッツ

函館セット: ビール・うに丼・ドーナッツ

ItemsTable

menu, price,  id

札幌セット    1,700    1
知床セット    2,800    2
函館セット    3,900    3
ラーメン    800    4
ビール    600    5
タラバガニ丼    2000    6
うに丼    3000    7
ソフトクリーム    300    8
ドーナッツ    200    9

RelationshoipsTable

id set_menu_id food_id

1    1    4
2    1    5
3    1    8
4    2    5
5    2    6
6    2    9
7    3    5
8    3    7
9    3    9

 

Migrationファイルの構成は以下の通りになる。

ActiveRecord::Schema.define(version: 2021_09_14_134200)do

  create_table "items", force: :cascade do |t|
    t.string "menu"
    t.integer "price"
  // precisionはマイクロ秒 (6 桁)まで持つことを指している。mysql5.6.4から採用された。
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
end

  create_table "relationships", force: :cascade do |t|
    t.integer "set_menu_id", null: false
    t.integer "food_id", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["food_id"], name: "index_relationships_on_food_id"
    t.index ["set_menu_id"], name: "index_relationships_on_set_menu_id"
endend

次にItemモデルとRelationshipモデルに関してで、以下のとおり実装することができる。

class Item < ApplicationRecord

# セットに属するfood_idを取得するassociation
# 自分のテーブル内に外部キー"set_menu_id"置くことで、各商品と紐付けるフックを作成できてる。
  has_many :food_relationships, class_name: 'Relationship', foreign_key: 'set_menu_id', dependent: :destroy

# 各商品が属するset_menu_idを取得するassociation
# 自分のテーブル内に外部キー"food_id"を置くことで、各メニューと紐付けるフックを作成できてる。
  has_many :set_menu_relationships, class_name: 'Relationship', foreign_key: 'food_id', dependent: :destroy

#  札幌セット(set_menu)からラーメン・ビール・ソフトクリーム(food)を取得する
# has_many :紐付け対先のテーブル名, through: :中間テーブル名

  has_many :foods, through: :food_relationships

# ラーメン(food)から札幌セット(set_menu)を取得する
  has_many :set_menus, through: :set_menu_relationships
end
class Relationship < ApplicationRecord
// 中間テーブルは紐付けたいテーブル達のidのみを格納する場所
  belongs_to :food, class_name: 'Item'
  belongs_to :set_menu, class_name: 'Item'
end

自己結合って結局何

「同一テーブル内にあるデータ同士の関連性を、idを振ることによって判別しやすくすること。」なのでは。ないか。な。。自分で実装してないからわからん、、

自己結合を使って、カレーセットとカレーを関係付けてみた(フォローのassociationの勉強になるかも) | TechEssentials

Active Record の関連付け - Railsガイド