11 Stimmen

Rails SQL COUNT N+1 Ineffizienz

Ich habe einen Blog. Auf meiner Index-Seite werden alle Blog-Einträge angezeigt. Für jeden Blogbeitrag zähle ich die Anzahl der Kommentare zu diesem Beitrag. Dies führt zu einem N+1-Problem. Meine Abfragen sehen wie folgt aus:

SELECT "blog_posts".* FROM "blog_posts" WHERE ("blog_posts"."published" = 't') ORDER BY published_at DESC
SELECT "users".* FROM "users" WHERE ("users"."id" IN (1, 2, 3)) 
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 10)
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 9)
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 8)
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 2)
SELECT COUNT(*) FROM "blog_comments" WHERE ("blog_comments".blog_post_id = 7) 

Gibt es eine Möglichkeit in Rails, die COUNT in der gleichen Weise, die ich die Benutzer (SQL-Zeile 2) einschließen?

21voto

Heikki Punkte 15089

Sie können den Zähler-Cache verwenden: http://guides.rubyonrails.org/association_basics.html#counter_cache

"Mit dieser Deklaration hält Rails den Cache-Wert auf dem neuesten Stand und gibt diesen Wert dann als Antwort auf die size-Methode zurück."

class BlogPost < ActiveRecord::Base
  has_many :blog_comments
end

class BlogComment < ActiveRecord::Base
  belongs_to :blog_post, :counter_cache => true
end

Ein Blogbeitrag hätte eine Spalte mit dem Namen blog_comments_count .

3voto

Phrogz Punkte 283167

Im Allgemeinen möchten Sie eine SQL-Abfrage wie die folgende:

  SELECT COUNT(*), blog_post_id
    FROM blog_comments
GROUP BY blog_post_id;

Damit können Sie einen Hash aus blog_post_id und der Anzahl der Kommentare erstellen.

2voto

Ankit Bohra Punkte 1031

Sie können auch etwas in dieser Art sehen:

BlogComment.group('blog_post_id').count

In reiner Rails-Manier :)

2voto

user670908 Punkte 181

Sie können verwenden dase Edelstein oder eine der Techniken, die in dieses Video .

Beispiel mit Dase:

Author.includes_count_of(:articles).each do |author| puts "#{author.name} has #{author.articles_count} articles" end

0voto

the Tin Man Punkte 154584

Dies ist eine ActiveRecord-Abfrage, die meine Tabelle "site_access_log" durchsucht, die die Webzugriffe auf eine Website enthält.

Er wählt das Feld "remote_addr" aus den ersten 15 Datensätzen aus, zusammen mit der Zählung für diese IP, sortiert in absteigender Reihenfolge nach der Zählung, gefolgt von aufsteigender Reihenfolge für IP-Nummern, die die gleiche count_number haben.

Ich verwende Postgres, das IPv4-Nummern versteht, also habe ich das Feld in eine inet um eine korrekte Sortierung nach Wert und nicht nach ASCII-Wert zu ermöglichen. Wenn Ihre Datenbank keine inet-Werte unterstützt, können Sie jederzeit mit der Socket- oder IPSocket-Bibliothek von Ruby von IP nach inet konvertieren und die abgerufenen Ergebnisse dann sortieren.

@remote_addr_results = SiteAccessLog.all(
  :select     => 'remote_addr, count(remote_addr) as remote_addr_count',
  :group      => :remote_addr,
  :order      => 'remote_addr_count desc, cast(remote_addr as inet)',
  :limit      => 15
)
puts @remote_addr_results.map{ |r| r.remote_addr_count << ' : ' << r.remote_addr }

>> 985 : 68.228.61.183
>> 572 : 205.203.134.197
>> 500 : 68.32.220.153
>> 460 : 72.200.64.128
>> 281 : 24.121.196.194
>> 262 : 99.91.9.155
>> 241 : 68.99.237.178
>> 213 : 68.99.119.137
>> 208 : 70.167.157.162
>> 204 : 201.165.6.2
>> 164 : 72.201.233.147
>> 155 : 75.245.177.106
>> 150 : 97.123.246.154
>> 149 : 201.165.190.98
>> 145 : 74.37.165.220

Das generierte SQL sieht wie folgt aus:

SELECT remote_addr, count(remote_addr) as remote_addr_count                                                                       
FROM "site_access_logs"                                                                                                           
GROUP BY remote_addr                                                                                                              
ORDER BY remote_addr_count desc, cast(remote_addr as inet)                                                                        
LIMIT 15

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X