LoginSignup
44
39

More than 5 years have passed since last update.

:primary_keyと:foreign_keyを同時指定して、主キーではないけど共通なカラム同士で関連付けする

Last updated at Posted at 2015-05-27

(Rails + MySQL を想定しています)

↓のような3TBLがあるとする

class CreateAtbl < ActiveRecord::Migration
  def change
    create_table :atbls do |t|
      t.string :name
    end
  end
end

class CreateBtbl < ActiveRecord::Migration
  def change
    create_table :btbls do |t|
      t.string :name
      t.integer :b_a_id  # AテーブルのIDを保持
    end
  end
end

class CreateCtbl < ActiveRecord::Migration
  def change
    create_table :ctbls do |t|
      t.string :name
      t.integer :c_a_id  # AテーブルのIDを保持
    end
  end
end
class Atbl < ActiveRecord::Base
  has_many :btbls, foreign_key: "b_a_id"
  has_many :ctbls, foreign_key: "c_a_id"
end

class Btbl < ActiveRecord::Base
  belongs_to :atbl, foreign_key: "b_a_id"

  ### ここに Ctbl との has_many を定義したい ###

end

class Ctbl < ActiveRecord::Base
  belongs_to :atbl, foreign_key: "c_a_id"
end
# atbls
id  name
1   A1

# btbls
id  name    b_a_id
2   B1          1

# ctbls
id  name    c_a_id
3   C1          1

Bテーブル => Cテーブルのhas_many関連 をどう設定するか?

has_many :ctbls, through: :atblを使う

  • BテーブルとCテーブルはお互いにAテーブルのIDを保持しているので、:throughでAテーブルを経由すれば紐付く
class Btbl < ActiveRecord::Base
  belongs_to :atbl, foreign_key: "b_a_id"

  has_many :ctbls, through: :atbl # ★★★
end
[Ruby2.2.2][Rails4.2.1](beers|test_arel)> bdata = Btbl.find(2)
  Btbl Load (0.2ms)  SELECT  `btbls`.* FROM `btbls` WHERE `btbls`.`id` = 2 LIMIT 1
+----+------+--------+
| id | name | b_a_id |
+----+------+--------+
| 2  | B1   | 1      |
+----+------+--------+
1 row in set

[Ruby2.2.2][Rails4.2.1](beers|test_arel)> bdata.ctbls
  Ctbl Load (0.3ms)  SELECT `ctbls`.* FROM `ctbls` INNER JOIN `atbls` ON `ctbls`.`c_a_id` = `atbls`.`id` WHERE `atbls`.`id` = 1
+----+------+--------+
| id | name | c_a_id |
+----+------+--------+
| 3  | C1   | 1      |
+----+------+--------+
1 row in set

しかし、 お互い同じ内容のカラムを保持しているのにわざわざ経由テーブルと INNER JOIN するのは無駄
btbls.b_a_idctbls.c_a_id を直接結合したい

has_many :ctbls, primary_key: "b_a_id", foreign_key: "c_a_id"を使う

  • has_many :紐付け先, primary_key: "自TBLの結合用カラム", foreign_key: "紐付け先TBLの結合用カラム と書くと良い
class Btbl < ActiveRecord::Base
  belongs_to :atbl, foreign_key: "b_a_id"

  has_many :ctbls, primary_key: "b_a_id", foreign_key: "c_a_id" ## ★★★
end
[Ruby2.2.2][Rails4.2.1](beers|test_arel)> bdata = Btbl.find(2)
  Btbl Load (0.2ms)  SELECT  `btbls`.* FROM `btbls` WHERE `btbls`.`id` = 2 LIMIT 1
+----+------+--------+
| id | name | b_a_id |
+----+------+--------+
| 2  | B1   | 1      |
+----+------+--------+
1 row in set

[Ruby2.2.2][Rails4.2.1](beers|test_arel)> bdata.ctbls
  Ctbl Load (0.4ms)  SELECT `ctbls`.* FROM `ctbls` WHERE `ctbls`.`c_a_id` = 1
+----+------+--------+
| id | name | c_a_id |
+----+------+--------+
| 3  | C1   | 1      |
+----+------+--------+
1 row in set
  • INNER JOINしてないのでパフォーマンス的に:throughするよりも有利
    • ただし経由テーブルにデータが存在することがMUST、という場合には当然:throughすべき

:throughの使い方で疑問に思うことがあったので、調査メモとして残しとく

44
39
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
39