LoginSignup
19
7

More than 3 years have passed since last update.

joinsで複数のテーブルの結合をスマートに。

Posted at

はじめに

テーブルの結合に対して苦手意識があり、せっかく便利なActiverecordがあるのにfind_by_sqlで逃げてしまい、sqlをゴリゴリに書いて結合させていた。

joinsを使って、3つ以上のテーブルを結合する方法に向き合ったのでまとめる。

結合するうえで、foreign_key, primary_keyをどう設定していくのかを絡めて、例をとりあげる。

取り上げた例

「本」「レビュー」「レビューした人」の3つテーブルを扱う。
DBは下の画像のとおりに設定する。

スクリーンショット 2019-11-11 15.58.24.png

取り出したい条件を
「女性が評価4.0以上レビューした本のタイトル一覧」とする。

つまり
where句でgender = 'woman' かつ evaluation >= 4.0 を指定して、
selectをtitleとするイメージ。

ただ、これをどう結合して扱うかが問題。

modelを整える

関係性を把握

あるユーザーはいくつもレビューする
あるレビューは一人のユーザーによって作られる
あるレビューは一つの本を対象にする

関係性は次のようになる。

スクリーンショット 2019-11-11 16.33.58.png

ひとつの結合

usersとreviewsの関係性だけみてみる。
まずusersのmodel

users.rb
class User < ApplicationRecord
  has_many :reviews
end

has_manyなのでreviewsと複数形。
これを設定すればUser.joins(:reviews)が使えるようになる。
:reviewsはmodelで定義したとおりに複数形

User.joins(:reviews)
=> "SELECT `users`.* FROM `users` 
    INNER JOIN `reviews` ON `reviews`.`user_id` = `users`.`id`"

reviewsのmodelも設定してみる。

reviews.rb
class Review < ApplicationRecord
  belongs_to :user, optional: true
end

これを設定すればReview.joins(:user)が使えるようになる。
belongs_toだからuserと単数形。

Review.joins(:user)
=> "SELECT `reviews`.* FROM `reviews` 
    INNER JOIN `users` ON `users`.`id` = `reviews`.`user_id`"

任意の主キー,外部キー

primary_keyforeign_keyを使って、任意の外部キー code を設定する。

※ codeではなくbook_idとして紐付ければよいのだが、任意の外部キー設定をとりあげるためにcodeとしている。

reviews.rb
class Review < ApplicationRecord
  belongs_to :user, optional: true
  belongs_to :book, primary_key: :id, foreign_key: :code, optional: true
end
books.rb
class Book < ApplicationRecord
  has_many :reviews
end

このように設定すると複数の結合をBook.joins(reviews: :user)と書くことができる。

Book.joins(reviews: :user)
=> "SELECT `books`.* FROM `books` 
    INNER JOIN `reviews` ON `reviews`.`code` = `books`.`id` 
    INNER JOIN `users` ON `users`.`id` = `reviews`.`user_id`"

目的の条件で取り出す

目的としていた「女性が評価4.0以上レビューした本のタイトル一覧」を取り出すには、

Book
 .joins(reviews: :user)
 .select('books.title')
 .where('users.gender = ?', 'woman')
 .where('reviews.evaluation >= ?', 4.0)

=> "SELECT `books`.* FROM `books` 
    INNER JOIN `reviews` ON `reviews`.`code` = `books`.`id` 
    INNER JOIN `users` ON `users`.`id` = `reviews`.`user_id` 
    WHERE users.gender = 'woman' 
    AND reviews.evaluation >= 4.0"

以上。
これで複数にまたがる結合からの抽出が完了

19
7
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
19
7