🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Ruby on Rails

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

Q&A

解決済

2回答

954閲覧

グループ参加人数0のグループも一覧に表示する

shawn_709

総合スコア13

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Ruby on Rails

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

0グッド

0クリップ

投稿2020/11/28 04:18

編集2020/11/28 08:10

ご回答いただけるとありがたいです。よろしくお願いします!

実現したいこと

・グループの参加人数が多い順にグループ一覧ページに表示されるようにしたい(実現済)
・グループの参加人数が0のグループもグループ一覧ページにに表示されるようにしたい

発生している問題

グループ一覧ページに、グループの参加人数が多い順に上から表示するように以下のようにコードを書きました。
※参加人数が同じ場合は、作成した順。

controller

1#groups 2def list 3 @groups = Group.joins(:group_users).group(:group_id).order('count(group_id) DESC').order('created_at ASC') 4end

参加人数0のグループのidは中間テーブルのgroup_usersテーブルのgroup_idカラムにはありません(当たり前ですが)。
上記のように書いた結果、参加人数が0のグループのgroup_idが取得できず、表示されなくなります。
※ちなみに

.joins(:group_users).group(:group_id).order('count(group_id) DESC')

を書かなければ、グループ一覧に参加人数0のグループも表示されます。

該当のソースコード

controller

1#groups 2def list 3 @groups = Group.joins(:group_users).group(:group_id).order('count(group_id) DESC').order('created_at ASC') 4end

views

1#list.html.erb 2<div class="group_lists"> 3 <h1>グループ一覧</h1> 4 <div class="group_list"> 5 <% @groups.each do |group| %> 6 <div class="group-container"> 7 <div class="upper-group-container"> 8 <div class="group-content-left"> 9 <div class="participation-button"> 10 <% if user_signed_in? %> 11 <% unless GroupUser.exists?(group_id: group.id, user_id: current_user.id) %> 12 <div class="no_participation"> 13 <%= link_to '参 加', join_group_path(group) %> 14 </div> 15 <% else %> 16 <div class="participation"> 17 <%= link_to '参加中', "#" %> 18 </div> 19 <% end %> 20 <% end %> 21 </div> 22 <div class="number_of_user"> 23 参加人数: <%= group.users.length %> #この行で参加人数を表示 24 </div> 25 </div> 26(省略)

以上になります。ご助言いただけると幸いです。

##追加(マイグレーションとモデル)

migration

1#groups 2class CreateGroups < ActiveRecord::Migration[6.0] 3 def change 4 create_table :groups do |t| 5 t.string :name, null: false, unique: true 6 t.string :content 7 t.references :user, foreign_key: true 8 t.timestamps 9 end 10 end 11end 12 13#group_users 14class CreateGroupUsers < ActiveRecord::Migration[6.0] 15 def change 16 create_table :group_users do |t| 17 t.references :group, foreign_key: true 18 t.references :user, foreign_key: true 19 t.timestamps 20 end 21 end 22end

model

1#Group 2class Group < ApplicationRecord 3 has_many :group_users 4 has_many :users, through: :group_users, dependent: :destroy 5 has_many :tweets, dependent: :destroy 6 belongs_to :user 7 8 validates :name, presence: true, uniqueness: true 9end 10 11#GroupUser 12class GroupUser < ApplicationRecord 13 belongs_to :group 14 belongs_to :user 15end

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

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

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

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

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

guest

回答2

0

ベストアンサー

Group.select("groups.*, COUNT(group_users.id) users_count").left_joins(:group_users).group("groups.id").order("users_count desc")

これで期待通りに動くと思いますが、どうでしょうか。
select に COUNT(group_users.id) users_count を含め、
users_countでソートしています。

カウンターキャッシュ

上記のクエリは件数が増えると遅いです。
また、.count とするとエラーになります。そのためkaminariなどと使えない(未確認)。

railsにはカウンターキャッシュという機能があります。
これはgroup.users.countの結果をカラムに保存しておく機能です。

値の管理は勝手にやってくれます。

group.users_count # => 0
group.users << user
group.users_count # => 1

カウンターキャッシュを用いると、「0を含むusers.count順」のクエリは以下のように書けます。
.countできない問題もありません。

Group.order("users_count desc")

:counter_cache Railsガイド

投稿2020/11/30 08:57

neko_daisuki

総合スコア2090

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

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

shawn_709

2020/11/30 09:16

いつもいつも本当にありがとうございます!! 期待していた形になりました!感動です! カウンターキャッシュが何か全く理解できていないですが、しっかり勉強して理解できるようになりたいと思います!参考文献まで付けて頂き、ありがとうございます。
guest

0

コンソールに出力されたSQLを確認されたらわかるかと思いますが、
joinsだと確かinner joinが実行されたはず。
inner joinだと合致しないデータは取得されません。
left_joinsだとleft joinなので合致しないデータがあってもnullで取得されます。

投稿2020/11/28 06:30

m.ts10806

総合スコア80875

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

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

shawn_709

2020/11/28 06:50

ご丁寧にありがとうございます! @groups = Group.left_joins(:group_users)~~~~としたら、参加人数0のグループも一覧に表示することができました。 感動しました!ありがとうございます!!
m.ts10806

2020/11/28 06:53 編集

フレームワークからデータを操作するだけでなく、SQL自身も勉強するとより効果的に使えるようになると思います。私自身Railsはまともにやって2,3ヶ月ですが、他の言語やSQL自体の経験はそれなりにあるので割と「どうすれば取得したいデータが取得できるか」が分かるので調べもつきやすいです。
shawn_709

2020/11/28 07:15

ありがとうございます!すみません。一つ問題が起きました。重ね重ねすみません。 left_joinsで参加人数0のグループも一覧に表示することができたんですが、 1グループ限定の表示になってしまっています。 参加人数0のグループ全てを取得するためにはどうすれば良いでしょうか? 申し訳ないです。。。
m.ts10806

2020/11/28 07:54

その「1グループ限定の表示」が良く分かりません。
shawn_709

2020/11/28 07:57

すみません。 参加人数が0のグループが複数あるのですが、 その内の1つのグループしかグループ一覧に表示されない ということです。
m.ts10806

2020/11/28 07:58

ん- テーブル定義とリレーション次第かなぁと。 マイグレーションファイルとモデルを提示してください。
m.ts10806

2020/11/28 08:00

ここが関係してたりしませんか? <% unless GroupUser.exists?(group_id: group.id, user_id: current_user.id) %>
shawn_709

2020/11/28 08:07 編集

マイグレーションとモデルはここでは、マークダウンが使えないので、上を編集して書き足しました。 <% unless GroupUser.exists?(group_id: group.id, user_id: current_user.id) %> に関しては、参加しているか否かを判断するために記述しています。
m.ts10806

2020/11/28 08:18 編集

たぶんこんな感じのSQLになっていたらいけそうには思います。 select groups.id groups.group_name from groups left join users on users.id = groups.user_id group by groups.group_id あとそれなりに意図が分からないのが count(group_id) のこと。 「ユーザー数が多いグループの順」だったらもうちょっと複雑になりそうな気がします。
shawn_709

2020/11/28 08:24

SQLを全然理解していないので、まずそこからやります! すみません。手間を取らせてしまって。 count(group_id)のところは、はじめuser_idでやっていたのですが、エラーが出るので勘で色々変えてたら、group_idでエラーが出なくなり、参加人数の多い順に並んだのでそうしていました。
m.ts10806

2020/11/28 08:44

現状でコンソールに出てきているSQLを質問本文に追記してもらって良いですか? アソシエーションの結果にもなると思うので。 SQLは一番内側から実行されるのでorder byのところでcount()しても想定通りには動かなそうに思います。
shawn_709

2020/11/28 08:59

本当に初心者すぎて理解ができていないところが多いです。 「現状でコンソールに出てきているSQL」というものがどれに該当しているのかがわかっていません。すみません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問