LoginSignup
3
0

[rails] find_by_sqlで結合先テーブルのカラムを取得する

Last updated at Posted at 2023-12-16

はじめに

Active Recordで表現しきれないクエリを記述するときに活躍するfind_by_sqlですが、find_by_sqlを呼び出したモデル以外のテーブルのカラムを取得する際に少し悩んだのでそちらを共有します。

(※実際にfind_by_sqlを使用する場合は複雑なテーブル構造やクエリが想定されますが、今回は簡単のため単縦なテーブル構造とクエリを使用します。)

テーブル構成

usersテーブル
id name age
1 taro 20
2 hanako 22
postsテーブル
id title user_id
1 taro’s post 1
2 hanako's post 2

結合先テーブルのカラムを取得する

以下のように結合先のテーブルのカラムも取得するSELEC文を記述し、Post.find_by_sqlを実行して見ると戻り値はpostテーブルのidカラムとtitleカラムのみになってしまいます。

sql = <<~SQL.squish
    SELECT posts.title, users.name
    FROM posts JOIN users ON posts.user_id = users.id
SQL

Post.find_by_sql(sql)
# => [#<Post "id"=>nil, "title"=>"taro's post">, #<Post "id"=>nil, "title"=>"hanako's post">]

postモデルのfind_by_sqlメソッドを使用しているから結合先の別モデルのカラムは取得できないのか...と思っていたのですが違いました。
attributesを見てみるとしっかり属性としては持っているようでした。

Post.find_by_sql(sql)[0].attributes
# => {"id"=>nil, "title"=>"taro's post", "name"=>"taro"}

そのため、通常通りカラム名で値にアクセスすることができます。

sql = <<~SQL.squish
    SELECT posts.title, users.name
    FROM posts JOIN users ON posts.user_id = users.id
SQL

posts_with_user = Post.find_by_sql(sql)
# => [#<Post "id"=>nil, "title"=>"taro's post">, #<Post "id"=>nil, "title"=>"hanako's post">]

posts_with_user[0].name
# => "taro's post"

また、AS句を使用して別名でSELECTすることで、その別名で値にアクセスすることもできます。

sql = <<~SQL.squish
    SELECT posts.title AS new_title, users.name AS new_name
    FROM posts JOIN Users ON posts.user_id = users.id
SQL

posts_with_user = Post.find_by_sql(sql)

posts_with_user[0].new_title
# => "taro's post"
posts_with_user[0].new_name
# => "taro"

終わりに

ご覧いただきありがとうございました。
少しでも誰かの悩む時間を減らせていたら嬉しいです!

3
0
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
3
0