0

私はしばらくの間、同じ線に沿った問題に苦労してきました-レールで効率的なクエリを実行します。私は現在、500,000 レコードのモデルに対してクエリを実行し、返された結果に関する記述的な統計を取得しようとしています。

概要として、一連の基準に一致する多数の製品を抽出したいと考えています。私はしたい...

  • レコードの数を数えます (何もない場合は、特定のアクションを抑制したい)
  • 一致するレコードの最大価格と最小価格を特定し、特定の範囲内にあるアイテムの数を計算します

現状では、この一連のコマンドは、私が望んでいたよりもはるかに長い時間がかかり (私のデスクトップ コンピューターでローカルに実行すると 26000 ミリ秒)、8 つまたは 9 つのアクティブなレコード アクションが含まれ、それぞれに約 3000 ミリ秒かかります。

これを処理するのが非常に遅くなるために私が間違っていることはありますか? どんな提案も素晴らしいでしょう

私のコントローラーのコードは次のとおりです。

    filteredmatchingproducts = Allproduct.select("id, product_name, price")
    .where('product_name LIKE ? 
    OR (product_name LIKE ? AND product_name NOT LIKE ? AND product_name NOT LIKE ?       AND product_name NOT LIKE ? AND product_name NOT LIKE ? AND product_name NOT LIKE ?) 
    OR product_name LIKE ? OR product_name LIKE ? OR product_name LIKE ? OR product_name LIKE ? OR (product_name LIKE ? AND product_name NOT LIKE ?) OR product_name LIKE ?', 
    '%Bike Box', '%Bike Bag%', '%Pannier%', '%Shopper%', '%Shoulder%', '%Shopping%', '%Backpack%' , '%Wheel Bag%', '%Bike sack%', '%Wheel cover%', '%Wheel case%', '%Bike case%', '%Wahoo%', '%Bicycle Travel Case%')
    .order('price ASC')

    @selected_products = filteredmatchingproducts.paginate(:page => params[:page])  

    @productsfound = filteredmatchingproducts.count
    @min_price = filteredmatchingproducts.first
    @max_price = filteredmatchingproducts.last

    @price_range = @max_price.price - @min_price.price

    @max_pricerange1 = @min_price.price + @price_range/4
    @max_pricerange2 = @min_price.price + @price_range/2
    @max_pricerange3 = @min_price.price + 3*@price_range/4
    @max_pricerange4 = @max_price.price 

    if @min_price == nil
    #don't do anything - just avoid error
    else

    @restricted_products_pricerange1 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', 0 , @max_pricerange1).count
    @restricted_products_pricerange2 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', @max_pricerange1 + 0.01 , @max_pricerange2).count
    @restricted_products_pricerange3 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?',  @max_pricerange2 + 0.01 , @max_pricerange3).count
    @restricted_products_pricerange4 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?',  @max_pricerange3 + 0.01 , @max_pricerange4).count
    end

編集 明確にするために、私が持っている基本的な質問は、なぜこれらのクエリのそれぞれを大規模な Allproduct データベースで実行する必要があるのか​​ 、前者の結果に対して後者のクエリを実行する方法がないのですか (つまり、filteredmatchingproducts 自体を使用しないクエリごとに再計算します)? 他のプログラミング言語では、操作を実行する前に変数を再度計算するのではなく、変数を記憶し、それらの記憶された値の操作を実行できることに慣れています - これはRailsの考え方ではありませんか?

4

4 に答える 4

2

あなたが共有したコード スニペットには、あまりにも多くの問題があります。おそらく最も重要なことは、これは Rails 固有の最適化の問題ではなく、データベース構造と最適化の問題です。

インデックスを適用できないため、両側にアンパサンド (%) を使用して「like」クエリを使用すると、SQLLite で直線的な検索時間が発生します。理想的には、「Like」を使用して検索を適用するべきではありませんが、代わりに、AllProducts テーブルで product_category_id として参照され、インデックスが定義されている product_categories テーブルを定義する必要があります。

@products_found、@min_price、および @max_price 変数を初期化するには、次の操作を実行できます。

filteredmatchingproductlist = filteredmatchingproducts.to_a
@productsfound = filteredmatchingproductlist.count
@min_price = filteredmatchingproductlist.first
@max_price = filteredmatchingproductlist.last

これにより、ActiveRecord::Relation ではなく配列に対してこれらの操作を実行しているため、個別のクエリがトリガーされるのを回避できます。

結果はソートされるため、filteredmatchingproductlist 配列に古き良きバイナリ検索を適用し、カウントを計算して、コードの最後の 4 行と同じ結果を得ることができます。

@restricted_products_pricerange1 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', 0 , @max_pricerange1).count
@restricted_products_pricerange2 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', @max_pricerange1 + 0.01 , @max_pricerange2).count
@restricted_products_pricerange3 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?',  @max_pricerange2 + 0.01 , @max_pricerange3).count
@restricted_products_pricerange4 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?',  @max_pricerange3 + 0.01 , @max_pricerange4).count

最後に、カウントと全文検索が本当に必要な場合は、Sphinx や Solr などの検索エンジンを統合することをお勧めします。それを実装する方法については、http://pat.github.io/thinking-sphinx/searching.htmlを参照してください。

于 2013-09-16T08:40:48.730 に答える
0

非常に多くのクエリを実行する理由は、多くのクエリを実行するように要求しているためです。(また、すべてのLIKEs は処理を遅くする傾向があります。) これは、作成される各クエリの前にコメントを追加したコードです (合計 8​​ つ)。

filteredmatchingproducts = Allproduct.select("id, product_name, price")
.where('product_name LIKE ? 
OR (product_name LIKE ? AND product_name NOT LIKE ? AND product_name NOT LIKE ?       AND product_name NOT LIKE ? AND product_name NOT LIKE ? AND product_name NOT LIKE ?) 
OR product_name LIKE ? OR product_name LIKE ? OR product_name LIKE ? OR product_name LIKE ? OR (product_name LIKE ? AND product_name NOT LIKE ?) OR product_name LIKE ?', 
'%Bike Box', '%Bike Bag%', '%Pannier%', '%Shopper%', '%Shoulder%', '%Shopping%', '%Backpack%' , '%Wheel Bag%', '%Bike sack%', '%Wheel cover%', '%Wheel case%', '%Bike case%', '%Wahoo%', '%Bicycle Travel Case%')
.order('price ASC')

#!!!! this is a query "select ... offset x, limit y"
@selected_products = filteredmatchingproducts.paginate(:page => params[:page])  

#!!!! this is a query "select count ..."
@productsfound = filteredmatchingproducts.count
#!!!! this is a query "select ... order id asc, limit 1"
@min_price = filteredmatchingproducts.first
#!!!! this is a query "select ... order id desc, limit 1"
@max_price = filteredmatchingproducts.last

@price_range = @max_price.price - @min_price.price

@max_pricerange1 = @min_price.price + @price_range/4
@max_pricerange2 = @min_price.price + @price_range/2
@max_pricerange3 = @min_price.price + 3*@price_range/4
@max_pricerange4 = @max_price.price 

if @min_price == nil
#don't do anything - just avoid error
else

#!!!! this is a query "select ... where price BETWEEN X and Y"
@restricted_products_pricerange1 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', 0 , @max_pricerange1).count
#!!!! this is a query "select ... where price BETWEEN X and Y"
@restricted_products_pricerange2 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?', @max_pricerange1 + 0.01 , @max_pricerange2).count
#!!!! this is a query "select ... where price BETWEEN X and Y"
@restricted_products_pricerange3 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?',  @max_pricerange2 + 0.01 , @max_pricerange3).count
#!!!! this is a query "select ... where price BETWEEN X and Y"
@restricted_products_pricerange4 = filteredmatchingproducts.select("price").where('price BETWEEN ? and ?',  @max_pricerange3 + 0.01 , @max_pricerange4).count
end
于 2013-09-15T04:24:05.163 に答える