1
2

More than 3 years have passed since last update.

【Rails】ActiveRecordのよく使うメソッド集(モデル検索、テーブル結合など)

Last updated at Posted at 2020-09-12

ActiveRecordには、データベースからオブジェクトを取り出す
検索メソッドが多数用意されています。

検索メソッドはwhereやgroupといったコレクションを返したり、
ActiveRecord::Relationのインスタンスを返します。

またfindやfirstなどの1つのエンティティを検索するメソッドの場合、
そのモデルのインスタンスを返します。

単一のオブジェクトを取り出す

find

主キーで検索

# 主キーが1のユーザーを検索

User.find(1)

# SELECT * FROM users WHERE (users.id = 1) LIMIT 1

findメソッドで複数のオブジェクトを取得

# 主キーが1と10のユーザーを検索

User.find(1,10) # User.find([1,10])も可

# SELECT * FROM users WHERE (users.id IN (1,10))

findメソッドでマッチするレコードが見つからない場合、
ActiveRecord::RecordNotFound例外が発生します。

first

デフォルトでは主キー順の最初のレコードを取得

User.first

# SELECT * FROM users ORDER BY users.id ASC LIMIT 1

firstメソッドで返すレコードの最大数を指定して取得

User.first(3)

# SELECT * FROM users ORDER BY users.id ASC LIMIT 3

orderを使って順序を変更した場合、
firstメソッドはorderで指定された属性にしたがって最初のレコードを取得

User.order(:name).first

# SELECT * FROM users ORDER BY users.name ASC LIMIT 1

firstメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。

last

デフォルトでは主キーの順序にしたがって最後のレコードを取得

User.last

# SELECT * FROM users ORDER BY users.id DESC LIMIT 1

lastメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。

find_by

与えられた条件にマッチするレコードのうち、最初のレコードを取得

User.find_by(name: "hoge")

# SELECT * FROM users WHERE (users.name = "hoge") LIMIT 1

find_byメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。

条件

where

返されるレコードを制限するための条件を指定します。
SQL文でいうWHEREの部分に相当します。

User.where(name: "hoge")

# SELECT * FROM users WHERE (users.name = "hoge")

・NOT条件
SQLのNOTクエリはwhere.notで表せます。

User.where.not(name: "hoge")

# SELECT * FROM users WHERE (users.name != "hoge")

・OR条件
1つ目のリレーションでorメソッドを呼び出し、そのメソッドの引数に
2つ目のリレーションを渡します。

User.where(name: "hoge").or(User.where(name: "foo"))

# SELECT * FROM users WHERE (users.name = "hoge") OR (users.name = "foo") 

並び順

データベースから取り出すレコードを特定の順序で並び替え

# ひとかたまりのレコードを取り出し、created_atの昇順で並び替え

User.order(:created_at)

# SELECT * FROM users ORDER BY users.created_at ASC
複数のフィールドを指定して並び替え

User.order(name: :asc, created_at: :desc)

# SELECT * FROM users ORDER BY users.name ASC, users.created_at DESC

取り出すレコード数の上限を指定

limit

主キー順の最初から数えて5件のレコードを取得

User.limit(5)

# SELECT * FROM users LIMIT 5

テーブルの結合

joins

デフォルトは内部結合(INNER JOIN)
内部結合は「結合条件に一致するレコードのみを取得」
この場合だとpostsテーブルのuser_idと、usersテーブルのidを指定

# モデル.joins(:関連名)
User.joins(:posts)

# SELECT users.* FROM users INNER JOIN posts ON (posts.user_id = users.id) 

left_joins

左外部結合(LEFT OUTER JOIN)
「結合条件に一致するレコード」と「左側のテーブルにしかないレコード」を取得

User.left_joins(:posts)

# SELECT users.* FROM users LEFT OUTER JOIN posts ON (posts.user_id = users.id) 

関連付けを一括読み込みする

includes

一括読み込み(eager_loading)とは、Model.findによって返されるオブジェクトに関連付けられたレコードを読み込むためのメカニズムであり、できるだけクエリの回数を減らすようにします。

・N+1クエリ問題
以下のコードは、ユーザーを10人検索して郵便番号を表示します。
usersテーブルとaddressesテーブルの関係は1対1とします。


users = User.limit(10)

users.each do |user|
  puts user.address.postcode
end

このコードには問題があります。実行されたクエリの数が無駄に多いことです。
最初にユーザーを10人検索するのにクエリを1回発行し、次にそこから住所を取り出すのに
クエリを10回発行するので、合計11回のクエリが発行されます。

・N+1クエリ問題を解決する
ActiveRecordは、読み込まれるすべての関連付けを事前に指定することができます。
これはincludesを指定することで実現できます。
includesを指定すると、ActiveRecordは指定されたすべての関連付けが最小限のクエリ回数で
読み込まれるようにしてくれます。

上の例でいうと、User.limit(10)を書き直して、住所が一括で読み込まれるようにします。


users = User.includes(:address).limit(10)

users.each do |user|
  puts user.address.postcode
end

最初の例では11回もクエリが発行されましたが、今回の例では2回にまで減りました。


SELECT * FROM users LIMIT 10
SELECT addresses.* FROM addresses 
  WHERE (addresses.user_id IN (1,2,3,4,5,6,7,8,9,10))

参考

Railsガイド

1
2
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
1
2