LoginSignup
3
0

More than 1 year has passed since last update.

Railsで関連モデルのレコード数を使って検索を行いたい時のメモ

Last updated at Posted at 2021-10-25

TL;DR

# 基本スタイル
User.joins(:orders).group('users.id').having('COUNT(orders.id) > ?', 2)
=> User Load (0.9ms)  SELECT "users".* FROM "users" GROUP BY users.id HAVING (COUNT(orders.id) > 2)

# COUNT DISTINCT(いらないかも...)
User.joins(:orders).group('users.id').having('COUNT(DISTINCT orders.id) > ?', 2)
=> User Load (1.8ms)  SELECT "users".* FROM "users" INNER JOIN "orders" ON "orders"."user_id" = "users"."id" GROUP BY users.id HAVING (COUNT(DISTINCT orders.id) > 2)
# 数値カラムと比較(購入数と年齢を比較する謎仕様(笑))
User.joins(:orders).group('users.id').having('COUNT(orders.id) > users.age')
=> User Load (3.9ms)  SELECT "users".* FROM "users" INNER JOIN "orders" ON "orders"."user_id" = "users"."id" GROUP BY users.id HAVING (COUNT(orders.id) > users.age)

# 別の関連モデルのレコード数と比較(購入数とレビュー数を比較)
User.joins(:orders, :reviews).group('users.id').having('COUNT(orders.id) > COUNT(reviews.id)')
=> User Load (13.5ms)  SELECT "users".* FROM "users" INNER JOIN "orders" ON "orders"."user_id" = "users"."id" INNER JOIN "reviews" ON "reviews"."user_id" = "users"."id" GROUP BY users.id HAVING (COUNT(orders.id) > COUNT(reviews.id))

注意

GROUP BY ... HAVING ... の構文は or 系の構文とたまに干渉し、エラーが生じるので、Ransackなどで複雑な条件で検索したい時は要注意です。

余談

上記構文は自動的に関連レコードのないものを検索対象外にしたが、関連レコードがないことを検索したい場合は下記の構文でできます。

# おすすめ
User.left_outer_joins(:orders).where(orders: {id: nil})
=> User Load (1.1ms)  SELECT "users".* FROM "users" LEFT OUTER JOIN "orders" ON "orders"."user_id" = "users"."id" WHERE "orders"."id" IS NULL

# 一応こちらもできるが、無駄なSQLが発行される
User.includes(:orders).where(orders: {id: nil})
=> SQL (1.3ms)  SELECT "users"."id" AS t0_r0, "users"."created_at" AS t0_r1, "users"."updated_at" AS t0_r2 ... 

参考

3
0
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
3
0