0

すべての夜、

SQL平均クエリについて簡単な質問がありました。

多くの評価がある投稿モデルがあります。特定の投稿のすべての評価が合計され、平均の評価数で除算されます。したがって、この計算はpostモデルで宣言され、データベースに保存されます。私のインデックスビューでは、すべての投稿を、投稿者、名前、および投稿の評価db列からの評価とともに一覧表示します。

私の質問は、これらの値がデータベースに保存されているのに、なぜRailsがこれらのSQL平均クエリをすべて実行するのかということです。

私のコードは次のとおりです。

コンソールで

Post Load (1.0ms)  SELECT `posts`.* FROM `posts` ORDER BY posts.created_at ASC LIMIT 1
 => #<Post id: 48, user_id: 5, song_name: "Enter Sandman", song: "ewwe wer ere rr erew rewr r erw erw rewedwde", created_at: "2012-08-16 13:35:32", updated_at: "2012-08-22 21:11:34", rating: 5.0, ratings_count: 2> 

Post.rb

def rating
    if self.ratings.any?
      self.rating = self.ratings.average(:rating)
    end
  end

posts / index.html.erb

<legend>Music library</legend>  

    <%= will_paginate @paginate_posts %>

            <% if @paginate_posts.any? %>
                <% @paginate_posts.each do |post| %>
            <h4><%= link_to post.song_name, post %></h4>
                Author: <%= link_to post.user.name, "#" %><br/>
                    <% if post.rating == nil %>
                        No one has rated this yet<br/>
                    <% else %>
                        Rating: <%= post.rating %>/10<br/>
                    <% end %>
                <br/>
        <% end %>
<% end %>

<%= will_paginate @paginate_posts %>

post_controller.rbインデックスアクション(積極的な読み込みを試みましたが、平均クエリはまだあります)

def index
    @paginate_posts = Post.paginate(page: params[:page], per_page: 10).includes(:user).search(params[:search])
  end

クエリが多いSQLログ

Started GET "/" for 127.0.0.1 at 2012-08-22 22:51:38 +0100
Processing by PostsController#index as HTML
  Post Load (0.3ms)  SELECT `posts`.* FROM `posts` ORDER BY posts.created_at DESC LIMIT 10 OFFSET 0
  User Load (0.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (5)
   (0.3ms)  SELECT COUNT(*) FROM `posts` 
  Rendered posts/_copy.html.erb (0.1ms)
   (0.3ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 63
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 63
   (0.3ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 62
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 62
   (0.2ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 61
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 61
   (0.2ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 60
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 60
   (0.2ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 59
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 59
   (0.2ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 58
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 58
   (0.2ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 57
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 57
   (0.2ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 56
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 56
   (0.2ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 55
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 55
   (0.3ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 54
  CACHE (0.0ms)  SELECT AVG(`ratings`.`rating`) AS avg_id FROM `ratings` WHERE `ratings`.`post_id` = 54
  Rendered posts/index.html.erb within layouts/application (60.8ms)
  User Load (0.4ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 5 LIMIT 1
  Rendered layouts/_footer.html.erb (0.1ms)
Completed 200 OK in 2012ms (Views: 1871.7ms | ActiveRecord: 33.1ms)

もっとコードが必要な場合はいつものように叫んでください。どうもありがとう、アンディ

4

3 に答える 3

2

ここに、モデルのアクセサーをオーバーライドするratingというメソッドがあります。そのため、db列の「rating」がある場合でも、model.ratingを要求したときに呼び出されることはなく、メソッドは繰り返されます。再び評価。さらに、メソッドは保存されません(属性を設定するだけでは、データベースに保持されません。saveなどを呼び出す必要があります)。したがって、評価の値を保存したい場合は、そのメソッドを削除します。

投稿に評価が追加または変更された場合は、実際にこれを行う必要があります。その時点で、update_ratingなどを呼び出し(以下を参照)、post.save afterを呼び出していることを確認します(before_saveコールバックにするか、 self.update_attributeまたはself.saveexplitlyin update_rating)-これにより、変更がデータベースに保存され、次を使用できます。

<%= post.rating %>

キャッシュされた平均評価値として、ビューまたはその他の場所で。

ポストモデル:

before_save :update_rating

def update_rating
  if self.ratings.any?
    self.rating = self.ratings.average(:rating)
  end
end
于 2012-08-23T09:38:33.877 に答える
1

投稿をループします。呼び出す各ループ内で:

post.rating 

これにより、AVGSQLクエリが実行されます。

self.ratings.average(:rating)

保存されていません。計算されただけなので、'ifpost.rating='が機能します。

于 2012-08-23T08:57:48.647 に答える
1

私の経験では、@ Kenny-Grantのupdate_rating方法では、更新された評価を計算して永続化するときに、実際には平均に新しく追加された評価が含まれないことがわかりました。

これは私のために働いています:

after_save :update_rating!

def update_rating
  self.rating = ratings.average(:rating) || 0.0
end

def update_rating!
  self.update_columns(rating: update_score)
end

ratingsこれにより、この段階ではメモリ内ではなくDBへの呼び出しであるため、新しく追加されたレコードが表示されるようになります。すべてのコールバックをトリガーする追加のアクションをupdate_columns発生させることなく、レコードを書き換えることができます。列を書き込むときにこのメソッドsaveを使用すると、メモリ内の評価属性も設定されます。これは、後でモデルをリロードしない限り、設定されません。update_ratingupdate_columns

于 2014-01-10T14:26:34.080 に答える