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