💻

Active Recordのjoinsメソッドで複数テーブルを結合する

2022/11/03に公開

これは何?

RailsガイドActive Record クエリインターフェイス / 13.1.3 複数の関連付けを結合するを読んでいて、joinsメソッド配列とハッシュをどのように使えばいいか深く知りたくなったので、いくつかのテーブル構成で実際に試し、結果をまとめた記事です。

対象バージョン

  • ruby: 2.7.6
  • rails: 7.0.3.1

joinsメソッド内のルール

①複数のテーブルを結合する場合は、配列[]にする
②親テーブルに対して複数のテーブルを結合する場合は、配列[]を省略できる
③ネストして結合する場合は、ハッシュ{}にする
④親テーブルに対して結合するテーブルが全てネストしている場合は、ハッシュ{}を省略できる
⑤結合するテーブルが全てネストしている場合はハッシュ{}をまとめることができる

実例

対象のテーブル構成

- A
  - B
    - C
      - D
      - E
    - F
  - G
    - H
    - I
  • インデントで親子関係を表現しており、それぞれのテーブルは1:1の想定です。
  • 最終的には、上記のテーブル構成に対して、joinsメソッドのみで結合することを目指します。

例1

対象のテーブル構成

- A
  - B
  - G

実装方法

# ルール①を適用
A.joins([:b, :g])

# ルール②を適用
A.joins(:b, :g)

発行されるSQL

SELECT
  as.*
FROM
  as
  INNER JOIN bs ON bs.a_id = as.id
  INNER JOIN gs ON gs.a_id = as.id

例2

対象のテーブル構成

- A
  - B
    - C

実装方法

# ルール③を適用
A.joins({b: :c})

# ルール④を適用
A.joins(b: :c)
SELECT
  as.*
FROM
  as
  INNER JOIN bs ON bs.a_id = as.id
    INNER JOIN cs ON cs.b_id = bs.id

例3

対象のテーブル構成

- A
  - B
    - C
  - G

実装方法

# 例2に対して、ルール①を適用
A.joins([{b: :c}, :g])

# ルール②を適用
A.joins({b: :c}, :g)

発行されるSQL

SELECT
  as.*
FROM
  as
  INNER JOIN bs ON bs.a_id = as.id
    INNER JOIN cs ON cs.b_id = bs.id
  INNER JOIN gs ON gs.a_id = as.id

例4

対象のテーブル構成

- A
  - B
    - C
  - G
    - H

実装方法

# 例3に対して、ルール③を適用
A.joins([{b: :c}, {g: :h}])

# ルール②を適用
A.joins({b: :c}, {g: :h})

# ルール⑤を適用
A.joins({b: :c, g: :h})

# ルール④を適用
A.joins(b: :c, g: :h)

発行されるSQL

SELECT
  as.*
FROM
  as
  INNER JOIN bs ON bs.a_id = as.id
    INNER JOIN cs ON cs.b_id = bs.id
  INNER JOIN gs ON gs.a_id = as.id
    INNER JOIN hs ON hs.g_id = gs.id

例5

対象のテーブル構成

- A
  - B
    - C
    - F
  - G
    - H

実装方法

# 例4に対して、ルール①を適用
A.joins([{b: [:c, :f]}, {g: :h}])

# ルール②を適用
A.joins({b: [:c, :f]}, {g: :h})

# ルール⑤を適用
A.joins({b: [:c, :f], g: :h})

# ルール④を適用
A.joins(b: [:c, :f], g: :h)

発行されるSQL

SELECT
  as.*
FROM
  as
  INNER JOIN bs ON bs.a_id = as.id
    INNER JOIN cs ON cs.b_id = bs.id
    INNER JOIN fs ON fs.b_id = bs.id
  INNER JOIN gs ON gs.a_id = as.id
    INNER JOIN hs ON hs.g_id = gs.id

例6

対象のテーブル構成

- A
  - B
    - C
    - F
  - G
    - H
    - I

実装方法

# 例5に対して、ルール①を適用
A.joins([{b: [:c, :f]}, {g: [:h, :i]}])

# ルール②を適用
A.joins({b: [:c, :f]}, {g: [:h, :i]})

# ルール⑤を適用
A.joins({b: [:c, :f], g: [:h, :i]})

# ルール④を適用
A.joins(b: [:c, :f], g: [:h, :i])

発行されるSQL

SELECT
  as.*
FROM
  as
  INNER JOIN bs ON bs.a_id = as.id
    INNER JOIN cs ON cs.b_id = bs.id
    INNER JOIN fs ON fs.b_id = bs.id
  INNER JOIN gs ON gs.a_id = as.id
    INNER JOIN hs ON hs.g_id = gs.id
    INNER JOIN is ON is.g_id = gs.id

例7

対象のテーブル構成

- A
  - B
    - C
      - D
    - F
  - G
    - H
    - I

実装方法

# 例6に対して、ルール③を適用
A.joins([{b: [{c: :d}, :f]}, {g: [:h, :i]}])

# ルール②を適用
A.joins({b: [{c: :d}, :f]}, {g: [:h, :i]})

# ルール⑤を適用
A.joins({b: [{c: :d}, :f], g: [:h, :i]})

# ルール④を適用
A.joins(b: [{c: :d}, :f], g: [:h, :i])

発行されるSQL

SELECT
  as.*
FROM
  as
  INNER JOIN bs ON bs.a_id = as.id
    INNER JOIN cs ON cs.b_id = bs.id
      INNER JOIN ds ON ds.c_id = cs.id
    INNER JOIN fs ON fs.b_id = bs.id
  INNER JOIN gs ON gs.a_id = as.id
    INNER JOIN hs ON hs.g_id = gs.id
    INNER JOIN is ON is.g_id = gs.id

例8

対象のテーブル構成

- A
  - B
    - C
      - D
      - E
    - F
  - G
    - H
    - I

実装方法

# 例7に対して、ルール①を適用
A.joins([{b: [{c: [:d, :e]}, :f]}, {g: [:h, :i]}])

# ルール②を適用
A.joins({b: [{c: [:d, :e]}, :f]}, {g: [:h, :i]})

# ルール⑤を適用
A.joins({b: [{c: [:d, :e]}, :f], g: [:h, :i]})

# ルール④を適用
A.joins(b: [{c: [:d, :e]}, :f], g: [:h, :i])

発行されるSQL

SELECT
  as.*
FROM
  as
  INNER JOIN bs ON bs.a_id = as.id
    INNER JOIN cs ON cs.b_id = bs.id
      INNER JOIN ds ON ds.c_id = cs.id
      INNER JOIN es ON es.c_id = cs.id
    INNER JOIN fs ON fs.b_id = bs.id
  INNER JOIN gs ON gs.a_id = as.id
    INNER JOIN hs ON hs.g_id = gs.id
    INNER JOIN is ON is.g_id = gs.id

Discussion