こんにちは! フリーエンジニアの長瀬です。
みなさん、joinsは使っていますか?
joinsを使えば、テーブルの内部結合が簡単できます。
この記事では、joinsの使い方について
・joinsの基本
という基本的な内容から、
・joinsのさまざまな使い方
といった応用的な内容についても解説していきます。
joinsの基本
joinsとは
joinsはテーブル同士を内部結合して検索するためのメソッドです。
内部結合とは、各テーブル同士で関連づけられたidが一致しているものだけを使って新しいテーブルを作ることです。
なので、idが一致しないものは切り捨てられます。
テーブルがいくつかに分けられている場合、1つ1つから必要なデータを見つけるよりも、テーブルを1つにまとめてから見つけるほうが楽ですよね?
なので、結合してから検索することを可能にするメソッドが用意されているわけです。
結合の種類には内部結合のほかに、左外部結合と右外部結合があります。
内部結合が一致したものだけで新しいテーブルを作るのに対して、外部結合では優先テーブルを選択して結合することができます。
なので、優先したテーブルのデータは切り捨てられません。
優先したテーブルにしかないデータに対応する部分はNULLになります。
たととば、以下のような2つのCastleテーブルとOwnerテーブルがあったとします。
Castleテーブル
Ownerテーブル
これを内部結合すると、
このように、owener_idが一致するものだけが、表示されます。
onwer_idが一致しなかった首里城は切り捨てられました。
Castleテーブルを優先して外部結合すると
このように優先されたテーブルはすべて表示され、対応する部分がない場合にはNULLになります。(この場合owner_id 4には対応するものがない)
joinsとincludesの違い
joinsとincludesの違いについては以下の記事にまとめてありますので、こちらを参考にしてください。
joinsのさまざまな使い方
結合したテーブルの値を取得する方法
まずはテーブルを作成します。
こちらのコードをコマンドプロンプトに入力してテーブルを作成してください。
rails g model User name:string rails g model Post user_id:integer title:string month:integer rake db:migrate
これでnameのカラムを持つUserテーブルとuser_id、title、monthカラムを持つPostデーブルが作成されます。
次に実際にデータを入れていきます。
rails consoleでコンソールを起動し、以下のコードをコピペしてください。
User.create(name:"山田花子") User.create(name:"長瀬来") User.create(name:"安田一郎") User.create(name:"細田俊介") User.create(name:"安原庄之助") User.create(name:"中村拓郎") Post.create(user_id:1,title:"楽しい休日の過ごし方" ,month:8) Post.create(user_id:1,title:"先日の旅行での話" ,month:2) Post.create(user_id:2,title:"昨日の出来事" ,month:12) Post.create(user_id:3,title:"山登りに行きました" ,month:8) Post.create(user_id:5,title:"友人が結婚しました" ,month:4) Post.create(user_id:8,title:"最近少し気になったこと" ,month:1) Post.create(user_id:13,title:"ランニングのコツ" ,month:9)
各テーブルに必要なデータが入りました。
次にUser、Postテーブルを関連付けます。
models/user.rbに
has_many :posts
models/post.rbに
belongs_to :user
をそれぞれ入力してください。
これで、準備が整いました。
実際にjoinsを使っていきましょう。
とその前にここからはrails cosoleに取り出したデータを出力していくので
出力したデータの見栄えを良くにするために一つgemを追加します。
(すでにインストール済みの方は飛ばしてもらってかまいません。)
まず、Gemfileに
gem 'hirb' # 出力結果を表として出力するgem gem 'hirb-unicode' # マルチバイト文字の表示を補正するgem
を追記して、
コマンドプロンプトで
bundle install
を入力してインストールしてください。
そして最後にrails consoleでコンソールを開いた後、
Hirb.enable
と入力して、hirbのgemを有効にします。
これで出力の準備は完了です。
それではjoinsを使ってみましょう。
rails consoleでコンソールを起動した後、以下のコードを入力してください。
[joinsを使ったサンプルコード]
User.joins(:posts)
[実行結果]
User Load (0.5ms) SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" +----+------------+---------------------------+---------------------------+ | id | name | created_at | updated_at | +----+------------+---------------------------+---------------------------+ | 1 | 山田花子 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 1 | 山田花子 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 2 | 長瀬来 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 3 | 安田一郎 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 5 | 安原庄之助 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | +----+------------+---------------------------+---------------------------+ 5 rows in set
このように、idが一致しているカラムだけが取り出されて、そのほかは切り捨てられました。
例えば、Post.create(user_id:8,title:"最近少し気になったこと" ,month:1)のコマンドで入力したデータのidは8ですが、それに対応するidがUserテーブルにはないので、内部結合では表示されません。
また、joinsでselectメソッドを使うことによって、特定のカラムを選択して表示できます。
rails consoleでコンソールを起動した後、以下のコードを入力してください。
[joins+selectを使ったサンプルコード]
User.joins(:posts).select("users.name")
[実行結果]
User Load (0.2ms) SELECT users.name FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" +----+------------+ | id | name | +----+------------+ | | 山田花子 | | | 山田花子 | | | 長瀬来 | | | 安田一郎 | | | 安原庄之助 | +----+——————+
select(カラム名.テーブル名)で、特定のカラムだけを選んで表示できました。
検索条件を設定する方法
joinsとwhere句を組み合わせることによって、内部結合した後に条件を指定してデータ取り出せます。
rails consoleでコンソールを起動した後、以下のコードを入力してください。
[joins+where句を使ったサンプルコード]
User.joins(:posts).where(posts: { month: 8 })
[実行結果]
User Load (0.2ms) SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "posts"."month" = ? [["month", 8]] +----+----------+---------------------------+---------------------------+ | id | name | created_at | updated_at | +----+----------+---------------------------+---------------------------+ | 1 | 山田花子 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 3 | 安田一郎 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | +----+----------+---------------------------+---------------------------+ 2 rows in set
Post.create(user_id:1,title:"楽しい休日の過ごし方" ,month:8)
Post.create(user_id:3,title:"山登りに行きました" ,month:8)
で追加したデータのmonthは両方とも8なので、この2つが取り出されました。
このように、where句を使うことによって条件を指定できます。
joinsにて外部結合を実現する方法
rails4以前の方法
joinsは内部結合であると説明しましたが、実は書き方によっては左外部結合にもできます。
rails consoleでコンソールを起動した後、以下のコードを入力してください。
[joinsで左外部結合をするサンプルコード]
User.joins("LEFT OUTER JOIN posts ON users.id = posts.user_id")
[実行結果]
User.joins("LEFT OUTER JOIN posts ON users.id = posts.user_id") User Load (2.1ms) SELECT "users".* FROM "users" LEFT OUTER JOIN posts ON users.id = posts.user_id +----+------------+---------------------------+---------------------------+ | id | name | created_at | updated_at | +----+------------+---------------------------+---------------------------+ | 1 | 山田花子 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 1 | 山田花子 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 2 | 長瀬来 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 3 | 安田一郎 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 4 | 細田俊介 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 5 | 安原庄之助 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 6 | 中村拓郎 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | +----+------------+---------------------------+---------------------------+ 7 rows in set
LEFT OUTER JOINの部分で左外部結合を実現しています。
優先テーブルはUserなので、Userテーブルのカラムが優先して表示されます。
rail5にて追加された新たな方法
またrails5からはleft_outer_joinsというメソッドが追加されて直接SQL文を書かなくても左外部結合できるようになりました。
rails consoleでコンソールを起動した後、以下のコードを入力してください。
[生のSQL文を使わずにjoinsで左外部結合するサンプルコード]
User.left_outer_joins(:posts)
[実行結果]
User Load (0.3ms) SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id" +----+------------+---------------------------+---------------------------+ | id | name | created_at | updated_at | +----+------------+---------------------------+---------------------------+ | 1 | 山田花子 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 1 | 山田花子 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 2 | 長瀬来 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 3 | 安田一郎 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 4 | 細田俊介 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 5 | 安原庄之助 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | | 6 | 中村拓郎 | 2017-07-04 12:03:54 +0900 | 2017-07-04 12:03:54 +0900 | +----+------------+---------------------------+---------------------------+
User.joins("LEFT OUTER JOIN posts ON users.id = posts.user_id”)を用いた時と同じ結果になることが確認できます。
なので、Rails5以降を使っている場合はこちらのメソッドを使うようにしてください。
まとめ
いかがでしたでしょうか?
この記事では、joinsの使い方を解説しました。
joinsを使うことで、テーブル同士を内部結合してからデータを取り出せます。
テーブルを結合する際には、joinsの他にpreload,eager_load,includesがあります。
それぞれのメソッドを組み合わせて使うことが多いので、それぞれの特性を理解したうえで、組み合わせて使っていきましょう。
もしjoinsの使い方について忘れてしまったらこの記事を確認してくださいね!