0

私は一日中クエリをリファクタリングしようとしていました。20レコードのテーブルがロードされるまでに数分かかりました。最初に、私はこのstackoverflowの投稿でクエリを非難しました:

rails-Rails.cacheを使用するとエラーが発生します

ただし、いくつかのテストを行った後、クエリの読み込みが非常に速く、原因ではないことに気付きました。

次に、この投稿で説明したように、カミナリが何千ものレコードをルビーオブジェクトに入力していると思ったので、カミナリのせいにしました。

レールとカミナリ

しかし、それも間違っていました。カミナリは一度に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の各イテレーターを使用してすべてのレポートを調べ、次にそれらのレポートに関連付けられたアラートを使用して、「仮想フェンスの内側」にあるアラートを見つけます。次に、そのレポートの時間を保存し、2つの時間の差を取ります。2つの期間の間に多くのレポートがある場合、このロジックは永遠にかかるようです。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')

テーブル内の行数によっては遅くなる可能性がありますが、これは確かに少なくともある程度の影響を与えるはずです。この時点で満足できない場合は、テーブルのtimeおよびunit_id列にインデックスを追加し、テーブルの列にインデックスがあることを確認する必要があります。reportsalertsreport_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 に答える