Cet article présente une petite astuce pour éviter les requêtes N+1 dues à l'utilisation de .count() dans ActiveRecord.
Commentaires
Vous devez
vous inscrire
ou
vous connecter
pour poster un commentaire
Cet article présente une petite astuce pour éviter les requêtes N+1 dues à l'utilisation de .count() dans ActiveRecord.
De façon générale, ActiveRecord est magique mais ce n'est il est toujours intéressant, en fin de dev. de regarder en détail le SQL exact généré et envoyé à la DB (pas seulement sur ce point).
En tweakant un peu, il y a des gains de performance très substanciels à faire (parfois du x100). C'est une chose à laquelle on ne s'attache pas forcément au début du cycle de vie de l'appli (deadlines + tables vides), mais j'ai un portail de données avec plus de 4 millions de records sous le coude, et là ça devient vraiment nécessaire !
Il y a presque 13 ansEffectivement, après on trouve un certain nombre de gems qui aident dans ce process.
On trouve par exemple bullet pour les N+1 https://github.com/flyerhzm/bullet
Il y a également scrooge mais on dirait que ça n'a pas été mis à jour de puis un moment https://github.com/methodmissing/scrooge
Il y a presque 13 ansPlutôt que de compter la taille d'un tableau, j'utiliserai plutôt un counter_cache sur article (http://guides.rubyonrails.org/association_basics.html#belongs_to-association-reference 4.1.2.4 :counter_cache ) ou alors un COUNT sur la jointure :
Article.joins(:comments).select('COUNT(DISTINCT comments.id) AS mon_super_count_de_comments').all.each {|a| puts a.mon_super_count_de_comments}
L'approche envisagée dans l'article n'est valide que si on a besoin d'instancier les commentaires pour en faire autre chose que le count et qu'on ne veut pas mettre en place de counter_cache. Ca me semble être une exception plutôt que la règle comme le laisse penser l'article.
Il y a presque 13 ansHéhé le count réserve des surprises !
Autre petit point que je rencontre (mongoid) c'est quand je fais @articles = Article.all dans le contrôleur puis @articles.count (ça lance une requête de comptage) puis @articles.each {} (ça lance une requête pour récupérer les articles). Ce sont les joies du "late eval" de l'ORM. Du coup je force l'exécution de la requête en faisant @articles = Article.all.entries et le count se fera sur le tableau. Il y a bien des cas où c'est parfait.
Il y a presque 13 ans