質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

2399閲覧

Rais の構文で複数テーブルにまたがる条件で検索したい

退会済みユーザー

退会済みユーザー

総合スコア0

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2018/10/29 11:22

https://teratail.com/questions/153904

こちらで複数テーブルにまたがるSQLの書き方を教えていただいたのですが
これを Rails の ActiveRecord でかく方法がわかりません

というより回答でいただいた SQL が複雑過ぎて理解できないので
Rails の構文でかけば少しは理解できるかなと思って質問した次第です
(あるいは上記回答のSQL文にどこからどこまでが細かく()をいれてもらえるだけでもだいぶ読みやすくなるのですが
AS、GROUP、BY、HAVING、COUNTといったはじめてみる要素が多々でてくるためどこからどこまでが区切りなのか全くわからないです)

データベースは
user { id: int, ...その他データ }
user_status { id: int(incremental), user_id: int, status_id: int }
というテーブルカラムをもっていて

たとえば

[users table]
1
2
3

[user_statuses table]
user_id status_id
1 1
1 2
1 3
2 2
2 3
2 4
3 3
3 4
3 5

(ユーザ1 はステータス 1,2,3
ユーザ2 はステータス 2,3,4
ユーザ3 はステータス 3,4,5
をもってる)

場合に status_list = [2,3] を検索キーとしてあたえたときにこの全部をもっているユーザを探して
この場合 ユーザ1 とユーザ2 (ユーザ3 はステータス 2 をもってないので除外) が検索結果となる

こういう検索を

@users = User.where(...) # 他の条件でしぼりこみ
@users = @users.where( ステータス 2, 3 をもってるユーザを絞り込みたい )

という Rails の構文でかくにはどうしたらいいんでしょうか

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

db/schema.rb

ruby

1ActiveRecord::Schema.define(version: 2018_10_29_120428) do 2 3 create_table "user_statuses", force: :cascade do |t| 4 t.integer "user_id" 5 t.integer "status" 6 t.datetime "created_at", null: false 7 t.datetime "updated_at", null: false 8 t.index ["user_id"], name: "index_user_statuses_on_user_id" 9 end 10 11 create_table "users", force: :cascade do |t| 12 t.string "name" 13 t.date "birthday" 14 t.datetime "created_at", null: false 15 t.datetime "updated_at", null: false 16 end 17 18end

にたいして、 db/seed.rb を

ruby

1user = User.create(name: "name_a") 2user.user_statuses = [ 3 UserStatus.create(status:1), 4 UserStatus.create(status:2), 5 UserStatus.create(status:3) 6] 7user.save! 8 9user = User.create(name: "name_b") 10user.user_statuses = [ 11 UserStatus.create(status:2), 12 UserStatus.create(status:3), 13 UserStatus.create(status:4) 14] 15user.save! 16 17user= User.create(name: "name_c") 18user.user_statuses = [ 19 UserStatus.create(status:3), 20 UserStatus.create(status:4), 21 UserStatus.create(status:5) 22] 23user.save!

として、 rails db:migre と rails db:seeds して DB を作りました。

User.joins(:user_statuses).where("user_statuses.status in (2,3)").group(:name).having('COUNT(*) == 2').order(:name)
とすると、つぎの SQL が 発行できます。

SELECT "users".* FROM "users" INNER JOIN "user_statuses" ON "user_statuses"."user_id" = "users"."id" WHERE (user_statuses.status in (2,3)) GROUP BY "users"."name" HAVING (COUNT(*) == 2) ORDER BY "users"."name" ASC

rails c で操作して試してみます。

イメージ説明

イメージ説明

追記:
joins を使わない方法も考えてみました。

User.where(id: UserStatus.where(status: [2,3]).group(:user_id).having("count(*)=2").select(:user_id)) は SELECT "users".* FROM "users"  WHERE "users"."id" IN    ( SELECT "user_statuses"."user_id"    FROM "user_statuses" WHERE "user_statuses"."status" IN (?,?) GROUP BY "user_statuses"."user_id" HAVING (count(*)=2) )

の QL を発行します。
イメージ説明

参考
slformatter といページをつかうと 長い SQL を整形してくれます。
http://www.dpriver.com/pp/sqlformat.htm

投稿2018/10/29 14:04

編集2018/10/30 13:19
katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/10/30 04:23

ありがとうございます group by に改行があるだけですごくわかりやすくなりました SELECT "users".* FROM "users" INNER JOIN "user_statuses" ON "user_statuses"."user_id" = "users"."id" users と user_statuses テーブルを user_id が同じもの同士で結合するって意味ですよね ユーザーテーブル user_id = [1, 2, 3] だったのが (user_id, status_id) 1 1 1 2 1 3 2 2 2 3 2 4 3 3 3 4 3 5 という状態になって WHERE (user_statuses.status in (2,3)) によって 1 2 1 3 2 2 2 3 3 3 というレコードになって GROUP BY "users"."name" で user_id でひとくくりにして 1 => (1, 2), (1, 3) 2 => (2, 2), (2, 3) 3 => (3, 3) となって HAVING COUNT で要素数が2のもの 1 => (1, 2), (1, 3) 2 => (2, 2), (2, 3) のユーザレコードがえられるという流れなんでしょうか ようやく group by の意味わかった気がします まだ INNER JOIN がなぜ必要かが微妙にしか把握できないんですが これらの操作をあくまでユーザレコード上の操作にして最後にユーザレコードを得るために必要ということなんでしょうか p_status 上で where p_status_id in (2,3) だと user_id は得られるけど p_status のレコードになってしまうので以降の users 上の where がかけない ということなんでしょうか 自分でかけるかはまだわからないですが… 本当にすごく丁寧にありがとうございました
katoy

2018/10/30 13:21

joins を使わない書き方を回答に追記しました。 他にもいろいろな書き方を考えることはできると思います。 "activerecord hasmany where" などで google 検索してみると良いです。
退会済みユーザー

退会済みユーザー

2018/10/31 04:42

こちらのほうがわかりやすいかもです というか一番最初にこのロジックで書こうと思っていたんですが具体的にどうかけばいいのかがわからなくて困っていました 何度も丁寧にありがとうございました おかげで group by と count の使い方は理解できた気がします
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問