0

我整天都在尝试重构我的查询。加载一个包含 20 条记录的表需要几分钟时间。起初,我在这个 stackoverflow 帖子中指责了查询:

rails - 使用 Rails.cache 会出错

然而,在做了一些测试之后,我意识到查询加载非常快,并不是罪魁祸首。

然后我把它归咎于 kaminari,因为我认为 kaminari 正在将数千条记录填充到 ruby​​ 对象中,正如我在这篇文章中解释的那样:

轨道和kaminari

但这也是错误的。Kaminari 一次只加载 20 条记录(我使用记录器检查加载了多少报告,结果显示为 20 条)。

所以最后我想我找到了真正的罪魁祸首。这是在该页面的分页期间发生的另一个查询。我意识到这个查询每次运行都需要 20 多秒的时间来加载:

def virtual_fence_duration
    inside_fence_time = nil
    outside_fence_time = nil

    outside_fence_time = self.time

    previous_reports = Report.where{(time    <  my{self.time}) &
                                   (unit_id == my{self.unit_id})}.order('time desc')

    previous_reports.each do |report|
      alerts = report.alerts.collect { |a| a.code_name }
      if alerts.include? "Inside virtual fence"
        inside_fence_time = report.time
        break
      end
    end

    if inside_fence_time && outside_fence_time
      "Virtual Fence Elapsed Time: #{(((outside_fence_time - inside_fence_time).to_i).to_f/60.0).ceil} minutes" 
    else
      ""
    end    
  end

基本上,当存在“超出虚拟围栏”的警报时,会调用此方法。我存储时间。然后我查询之前的所有报告(报告 has_many 警报)。然后我使用 ruby​​ 的每个迭代器来遍历每个报告,然后使用与这些报告关联的警报来查找“在虚拟围栏内”的警报。然后我存储该报告的时间并取两者之间的差异。如果在两个持续时间之间有很多报告,这种逻辑似乎会永远持续下去。在 sql (mysql) 或 ruby​​ 中是否有更有效的方法来执行此操作?

4

2 回答 2

0

首先,我不熟悉"=="在 mysql 中使用的查询。应该只是"="。其次,"my()"查询中使用的函数是什么?

除非我不理解您的问题或您正在做的高级事情,否则我认为与此类似的查询会为您提供所需的内容。

Report.where('time < ? and unit_id = ?', self.time, self.unit_id).order('time desc')

如果不是,请发布实际从日志文件运行的查询。

于 2013-03-20T21:20:03.330 回答
0

这似乎是一个 N+1 性能问题。尝试使用急切加载includes

previous_reports = Report.where{(time    <  my{self.time}) &
                               (unit_id == my{self.unit_id})}.
                          includes(:alerts).order('time desc')

这肯定至少会产生一些影响,尽管它可能仍然很慢,具体取决于表中的行数。如果此时您不满意,则需要为表的timeunit_id列添加索引reports,并检查alerts表在列上是否有索引report_id

您还可以在单​​个查询中执行所有这些逻辑,这应该比当前方法有显着改进。当然,拥有正确的索引也不会受到伤害:

prev_report = Report.joins(:alerts)
                where{time < my{self.time} &&
                      unit_id == my{self.unit_id} &&
                      alerts.code_name.like '%Inside virtual fence%'}.
                order("reports.time DESC").first

inside_fence_time = prev_report.time if prev_report
于 2013-03-20T21:20:09.630 回答