波打際のブログさん

主に、プログラミング備忘録など。

レールから外れないためのActiveRecordリファレンス

はじめに

 RailsActiveRecordはとても優秀な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


以上です。今後、他のクエリ生成方法を使用したら随時追加していきます。