Question

J'ai essayé d'enchaîner des requêtes en utilisant Arel champs, au lieu d'utiliser une logique de longue haleine je l'ai écrit dans le contrôleur. Mais les champs d'application sont plus lentes que simplement obtenir tous les enregistrements et tamiser ensuite à travers eux avec une certaine logique. Je me demande donc pourquoi les étendues sont mieux.

Voici ce que je fais:

  • une question a beaucoup de réponses
  • une réponse appartient à une question
  • une question a une colonne "question_type" que j'utilise pour trier

Tout d'abord, les champs d'application façon ...

question.rb:

scope :answered, joins(:answers).order('answers.created_at desc')
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")

questions_controller.rb:

@dogs_recently_answered = Question.answered.dogs.uniq[0..9]
@cats_recently_answered = Question.answered.cats.uniq[0..9]
@mermaids_recently_answered = Question.answered.mermaids.uniq[0..9]

Ensuite, dans la vue, le cycle I à ces variables d'instance (qui sont maintenant des tableaux contenant au maximum 10 éléments) et afficher les résultats.

Voici le temps qu'il faut pour charger la page (cinq fois différents):

Terminé 200 OK à 535ms (Vues: 189.6ms | ActiveRecord: 46.2ms)

Terminé 200 OK à 573ms (Vues: 186.0ms | ActiveRecord: 46.3ms)

Terminé 200 OK à 577ms (Vues: 189.0ms | ActiveRecord: 45.6ms)

Terminé 200 OK à 532ms (Vues: 182.9ms | ActiveRecord: 46.1ms)

Terminé 200 OK à 577ms (Vues: 186.7ms | ActiveRecord: 46.9ms)

Maintenant, le chemin du contrôleur désordre ...

@answers = Answer.order("created_at desc")
@all_answered = []
@answers.each {|answer| @all_answered << answer.question}
@recently_answered = @all_answered.uniq
@dogs_all_answered = []
@cats_all_answered = []
@mermaids_all_answered = []
@recently_answered.each do |q|
  if q.question_type == "dogs"
    @dogs_all_answered << q
    @dogs_recently_answered = @dogs_all_answered[0..9]
  elsif q.question_type == "cats"
    @cats_all_answered << q
    @cats_recently_answered = @cats_all_answered[0..9]
  elsif q.question_type == "mermaids"
    @mermaids_all_answered << q
    @mermaids_recently_answered = @mermaids_all_answered[0..9]
  end
end

Et voici le temps qu'il faut pour charger la page maintenant (à cinq reprises):

Terminé 200 OK à 475ms (Vues: 196.5ms | ActiveRecord: 34.5ms)

Terminé 200 OK à 480ms (Vues: 200.4ms | ActiveRecord: 36.4ms)

Terminé 200 OK à 434ms (Vues: 198.2ms | ActiveRecord: 35.8ms)

Terminé 200 OK à 475ms (Vues: 194.2ms | ActiveRecord: 36.4ms)

Terminé 200 OK à 475ms (Vues: 195.0ms | ActiveRecord: 35.4ms)

Alors ...

En plus de lisibilité, ce qui est à gagner en aiguisant la requête avec une portée? Est-il fini par devenir plus rapide quand il y a plus de disques?

Était-ce utile?

La solution

D'abord, je ne suis pas sûr de comprendre comment une question peut être autre que unique, donc je regarde essayer d'enlever cela. Je ne sais pas la logique de vos données, de sorte que pourrait ne pas être applicable, mais il est une étape supplémentaire que vous pourriez être en mesure d'éviter.

Voici comment j'aborder:

scope :answered, joins(:answers).order('answers.created_at desc')
scope :recent, take(10)
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")

@dogs_recently_answered = Question.answered.dogs.recent
@cats_recently_answered = Question.answered.dogs.recent
@mermaids_recently_answered = Question.answered.dogs.recent

Cette décale la partie TOP de la requête à la base de données où il appartient plutôt que de chercher de tous des lignes puis jeter tout sauf 10. En fonction de vos critères uniquing, vous pouvez utiliser aussi une portée comme

scope :unique, select('DISTINCT column_name')

et ensuite vous pouvez utiliser Question.cats.unique.recent et obtenir en une seule requête rapide qui tire parti de l'algèbre relationnelle que les systèmes de base de données sont conçus pour.

Autres conseils

Je pense que la raison pour laquelle les champs d'application sont plus lents dans ce cas est parce qu'ils donnent lieu à 3 requêtes de base de données distincts, tandis que l'autre approche utilise les connaissances que les trois résultats peuvent être satisfaits par la seule requête que vous utilisez.

En supposant que est le cas, il est pas surprenant que les champs d'application sont en train de faire 3 requêtes distinctes puisque le système ne sait pas quand vous appelez la première que vous allez appeler les autres juste après. Peut-être il y a une stratégie d'optimisation qui serait raisonnable pour ce scénario, mais je ne sais pas qui implémente ActiveRecord il.

Quoi qu'il en soit, c'est un inconvénient de la portée dans ce cas particulier. Je aime portées parce qu'ils sont propres / claire, flexible et encapsulées une abstraction nommée pour une requête. AFAICT, dans de nombreux scénarios ils ne sont pas sensiblement plus lent que l'équivalent requête directe.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top