レールから外れないためのActiveRecordリファレンス
はじめに
RailsのActiveRecordはとても優秀なORMですが、使い方を理解せずに生のSQLを書いてしまうと、DBアダプターを変更した時に正しく動作することが保証されません。
今回はActiveRecordのレールから外れてしまいそうになった時、護輪軌条となるように、過去のプロダクト開発で使用したクエリの生成方法についてまとめてみました。
前提条件
ショッピングサイトのユーザー情報と購入情報を格納する下記のようDBテーブルとクラスをイメージしてください。
ユーザー情報
usersテーブル
id | ユーザID |
---|---|
name | 名前 |
age | 年齢 |
Userクラス
class User < ActiveRecord::Base has_many :orders end
購入情報
ordersテーブル
user_id | ユーザID |
---|---|
price | 購入した価格 |
created_at | 購入日 |
Orderクラス
class Order < ActiveRecord::Base belongs_to :user end
使用方法一覧
〜以外(否定)
例:20歳以外のユーザ一覧を取得する。
User.where.not(age: 18) #=> SELECT "users".* FROM "users" WHERE ("users"."age" != 20)
もしくは(OR条件)
例:18歳もしくは20歳のユーザー覧を取得する。
User.where(id: [18,20]) #=> SELECT "users".* FROM "users" WHERE "users"."id" IN (18, 20)
以上、以下
例:2015年以降の購入情報を取得する。
Order.where(Order.arel_table[:created_at].gt(Time.zone.local(2015))) # => SELECT "orders".* FROM "orders" WHERE ("orders"."created_at" > '2015-01-01 00:00:00.000000')
これは仕方がないのでarelを使用します。
以上という条件は`gt`、以下の場合は`lt`を使用します。
また、それも含む場合(`>=`)はそれぞれ `gteq` `lteq` を使用します。
重複を削除
例:5000円ピッタリ購入したユーザのIDを重複しないように取得する。
Order.where(price: 5000).select(:user_id).uniq #=> SELECT DISTINCT "orders"."user_id" FROM "orders" WHERE "orders"."price" = 5000
pluckしてユーザIDを取得するときなどに、重複をなくします。
サブクエリ
例:20歳のユーザの購入情報を取得する。
Order.where(user_id: User.where(age: 20).select(:id)) #=> SELECT "orders".* FROM "orders" WHERE "orders"."user_id" IN (SELECT "users"."id" FROM "users" WHERE "users"."age" = 20)
内部結合
例:購入金額が5000円ピッタリのユーザーを内部結合して取得する。
User.all.joins(:orders).merge(Order.where(price: 5000)).references(:orders) #=> SELECT "users".* FROM "users" INNER JOIN "orders" ON "orders"."user_id" = "users"."id" WHERE "orders"."price" = 5000
以上です。今後、他のクエリ生成方法を使用したら随時追加していきます。