【Rails】ActiveRecordのよく使うメソッド集(モデル検索、テーブル結合など)
ActiveRecordには、データベースからオブジェクトを取り出す検索メソッドが多数用意されています。
検索メソッドはwhereやgroupといったコレクションを返したり、ActiveRecord::Relationのインスタンスを返します。
またfindやfirstなどの1つのエンティティを検索するメソッドの場合、そのモデルのインスタンスを返します。
単一のオブジェクトを取り出す
find
主キーで検索
1 2 3 4 |
# 主キーが1のユーザーを検索 User.find(1) # SELECT * FROM users WHERE (users.id = 1) LIMIT 1 |
findメソッドで複数のオブジェクトを取得
1 2 3 4 |
# 主キーが1と10のユーザーを検索 User.find(1,10) # User.find([1,10])も可 # SELECT * FROM users WHERE (users.id IN (1,10)) |
findメソッドでマッチするレコードが見つからない場合、
ActiveRecord::RecordNotFound例外が発生します。
first
デフォルトでは主キー順の最初のレコードを取得
1 2 3 |
User.first # SELECT * FROM users ORDER BY users.id ASC LIMIT 1 |
firstメソッドで返すレコードの最大数を指定して取得
1 2 3 |
User.first(3) # SELECT * FROM users ORDER BY users.id ASC LIMIT 3 |
orderを使って順序を変更した場合、
firstメソッドはorderで指定された属性にしたがって
最初のレコードを取得
1 2 3 |
User.order(:name).first # SELECT * FROM users ORDER BY users.name ASC LIMIT 1 |
firstメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。
last
デフォルトでは主キーの順序にしたがって最後のレコードを取得
1 2 3 |
User.last # SELECT * FROM users ORDER BY users.id DESC LIMIT 1 |
lastメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。
find_by
与えられた条件にマッチするレコードのうち、最初のレコードを取得
1 2 3 |
User.find_by(name: "hoge") # SELECT * FROM users WHERE (users.name = "hoge") LIMIT 1 |
find_byメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。
条件
where
返されるレコードを制限するための条件を指定します。
SQL文でいうWHEREの部分に相当します。
1 2 3 |
User.where(name: "hoge") # SELECT * FROM users WHERE (users.name = "hoge") |
・NOT条件
SQLのNOTクエリはwhere.notで表せます。
1 2 3 |
User.where.not(name: "hoge") # SELECT * FROM users WHERE (users.name != "hoge") |
・OR条件
1つ目のリレーションでorメソッドを呼び出し、そのメソッドの引数に
2つ目のリレーションを渡します。
1 2 3 |
User.where(name: "hoge").or(User.where(name: "foo")) # SELECT * FROM users WHERE (users.name = "hoge") OR (users.name = "foo") |
並び順
データベースから取り出すレコードを特定の順序で並び替え
1 2 3 4 5 |
# ひとかたまりのレコードを取り出し、created_atの昇順で並び替え User.order(:created_at) # SELECT * FROM users ORDER BY users.created_at ASC |
1 2 3 4 5 |
複数のフィールドを指定して並び替え User.order(name: :asc, created_at: :desc) # SELECT * FROM users ORDER BY users.name ASC, users.created_at DESC |
取り出すレコード数の上限を指定
limit
主キー順の最初から数えて5件のレコードを取得
1 2 3 |
User.limit(5) # SELECT * FROM users LIMIT 5 |
テーブルの結合
joins
デフォルトは内部結合(INNER JOIN)
内部結合は「結合条件に一致するレコードのみを取得」
この場合だとpostsテーブルのuser_idと、usersテーブルのidを指定
1 2 3 4 |
# モデル.joins(:関連名) User.joins(:posts) # SELECT users.* FROM users INNER JOIN posts ON (posts.user_id = users.id) |
left_joins
左外部結合(LEFT OUTER JOIN)
「結合条件に一致するレコード」と「左側のテーブルにしかないレコード」を取得
1 2 3 |
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とします。
1 2 3 4 5 6 7 |
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)を書き直して、住所が一括で読み込まれるようにします。
1 2 3 4 5 6 7 |
users = User.includes(:address).limit(10) users.each do |user| puts user.address.postcode end |
最初の例では11回もクエリが発行されましたが、今回の例では2回にまで減りました。
1 2 3 4 5 |
SELECT * FROM users LIMIT 10 SELECT addresses.* FROM addresses WHERE (addresses.user_id IN (1,2,3,4,5,6,7,8,9,10)) |
最後に
自分自身、無駄に多くクエリを発行するコードを書いてしまうことがありました。今回学んだことを生かして、モデル検索の性能にも気をつけたいと思います。
以上です!