5

我一直在尝试使用范围链接 Arel 查询,而不仅仅是使用我在控制器中编写的一些冗长的逻辑。但是范围比获取所有记录然后用一些逻辑筛选它们要慢。那么,我想知道为什么范围更好。

这就是我正在做的事情:

  • 一个问题有很多答案
  • 一个答案属于一个问题
  • 一个问题有一个“question_type”列,我用它来对其进行排序

首先,范围方式...

有问题.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]

然后在视图中,我循环遍历这些实例变量(现在是最多包含 10 个元素的数组)并显示结果。

以下是加载页面所需的时间(五次不同):

在 535 毫秒内完成 200 次 OK(查看次数:189.6 毫秒 | ActiveRecord:46.2 毫秒)

在 573 毫秒内完成 200 次 OK(查看次数:186.0 毫秒 | ActiveRecord:46.3 毫秒)

在 577 毫秒内完成 200 次 OK(查看次数:189.0 毫秒 | ActiveRecord:45.6 毫秒)

在 532 毫秒内完成 200 次 OK(查看次数:182.9 毫秒 | ActiveRecord:46.1 毫秒)

在 577 毫秒内完成 200 次 OK(查看次数:186.7 毫秒 | ActiveRecord:46.9 毫秒)

现在,凌乱的控制器方式......

@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

以下是现在加载页面所需的时间(五次不同):

在 475 毫秒内完成 200 次 OK(查看次数:196.5 毫秒 | ActiveRecord:34.5 毫秒)

在 480 毫秒内完成 200 次 OK(查看次数:200.4 毫秒 | ActiveRecord:36.4 毫秒)

在 434 毫秒内完成 200 次 OK(查看次数:198.2 毫秒 | ActiveRecord:35.8 毫秒)

在 475 毫秒内完成 200 次 OK(查看次数:194.2 毫秒 | ActiveRecord:36.4 毫秒)

在 475 毫秒内完成 200 次 OK(查看次数:195.0 毫秒 | ActiveRecord:35.4 毫秒)

所以...

除了可读性之外,通过范围磨练查询还能获得什么?当有更多记录时,它最终会变得更快吗?

4

2 回答 2

5

首先,我不确定我是否理解一个问题除了独特性之外如何,所以我会考虑尝试删除它。我不知道您的数据的逻辑,因此这可能不适用,但这是您可以避免的额外步骤。

这是我将如何处理它:

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

这会将TOP查询的一部分转移到它所属的数据库,而不是获取所有行,然后丢弃除 10 之外的所有行。根据您的唯一标准,您还可以使用类似的范围

scope :unique, select('DISTINCT column_name')

然后您可以使用 Question.cats.unique.recent 并在一个快速查询中获取所有内容,该查询利用数据库系统设计的关系代数。

于 2010-11-30T14:40:01.230 回答
1

我认为在这种情况下范围较慢的原因是因为它们会导致 3 个单独的数据库查询,而另一种方法使用的知识是,您使用的单个查询可以满足所有三个结果。

假设是这种情况,范围执行 3 个单独的查询也就不足为奇了,因为系统不知道您何时调用第一个查询,然后您将立即调用其他查询。也许有一种优化策略对这种情况是明智的,但我不知道 ActiveRecord 实现了它。

无论如何,这是这种特殊情况下范围的一个缺点。我喜欢范围,因为它们干净/清晰、灵活并且为查询封装了一个命名抽象。AFAICT,在许多情况下,它们并不比等效的直接查询慢。

于 2010-11-30T14:20:05.787 回答