2010-02-26 60 views
1

我在Rails應用程序中有幾個模型。假設它是產品和銷售。獲取帶有統計信息的導軌模型

產品有很多銷售,並且銷售屬於產品。

我所希望做的是讓所有產品(可能條件下),每個產品旁邊的列表提供銷售,類似於數的計數:

  • 產品1(10)
  • 產品2(22)

雖然輸出是相當簡單的,我想不出一個很好的方式來收集數據,而無需使用SQL。

我還應該補充一點,我可能也想對銷售應用一個條件。

回答

1

如果你正確地宣佈associations,這是因爲以下簡單:

Product.find(:all, :include => :sales).each do |product| 
    puts product.name + " (#{product.sales.size})" 
end 

編輯:
關聯是可以搜索的,如果您需要申請條件豐富的館藏:

@products = Product.find(:all, :include => :sales) 
@products.each do |product| 
    # Find product sales only from yesterday 
    yesterdays_sales = product.sales.find(:all, :conditions => { :created_at => Date.yesterday } 
end 
+0

在這裏你有'N + 1'的問題。 – 2010-02-26 00:37:08

+0

沒有理由過早優化。協會是The Rails Way,任何看着他的代碼的Rails開發者都會很熟悉。如果這成爲瓶頸,他總是可以優化。 – 2010-02-26 00:42:28

+0

這不是一個過早的優化。只需使用':include'或':join'即可。你不會爲此花費任何時間。 爲什麼我們應該提前編寫錯誤的代碼,因爲現在我們可以用完全相同的工作編寫更好的代碼,因爲知道它是不好的。 – 2010-02-26 00:47:17

0

如果你不介意把從數據庫相關的銷售對象到應用程序,然後東西like this笑uld做這份工作:

但請留意它。

# Preload all the Sales for our Products, sow we won't end up with `N+1` issue. 
selected_products = Product.all :joins => :sales, :conditions => {'orders.successful' => true} 

# Then use it like a normal association: 
selected_products.each { |p| puts p.sales.size } 

問候,
德米特里。

+0

您的解決方案有N + 1個問題。 'Product.all'調用使用'WHERE'條件中的'join'表。在隨後對p.sales的調用中,將向數據庫提交新查詢以獲取與該產品關聯的銷售。我在這裏給出了詳細的答覆:(http://stackoverflow.com/questions/2338605/getting-a-rails-model-with-statistics/2339708#2339708)。 – 2010-02-26 05:44:11

+0

@KandadaBoggu,非常感謝。我相信'sales.size'不會執行查詢,因爲它應該用':join'加載。 需要去看看。 – 2010-02-26 05:52:35

+0

不用擔心。因爲你,我能夠證實我一直對'連接'有懷疑。 – 2010-02-26 07:41:46

1

如果您主要對計算每件產品的銷售量感興趣,則應該使用counter_cache功能。這將確保您始終從products表中獲取計數,而不是計算JIT計數。

# products table should have a column called sales_count 
class Product < ActiveRecord:Base 
    has_many :sales 
end 

class Sales < ActiveRecord:Base 
    belongs_to :product, :counter_cache => true 
end 

軌道將遞增/遞減在創建/刪除銷售sales_count列的照顧。 現在你可以通過執行獲得銷售數量如下:

Product.first.sales_count 

有條件地計算一個product對象的sales請執行以下操作:

Product.first.sales.count(:conditions => ["amount > ? ", 200]) 

有條件地計算了一批product就做如下:

#returns a ordered hash 
product_sales_count = Product.count(:joins => [:sales], :conditions => 
      ["amount > ? ", 200], :group =>"products.id").each do |product_id, sales_count| 
    p "Product #{product_id} = #{sales_count}" 
end 

# If you need the product objects get all of them in one call and match the count 
products = Product.find(:all, :joins => [:sales], :conditions => 
      ["amount > ? ", 200], :group =>"products.id").each do |product| 

    p "Product #{product.name} = #{product_sales_count[product.id]}" 

end 

這種方法將節省大量的旅行到數據庫。

+0

'Product.first.sales.count(:conditions => [「amount>?」,200])'將執行每個產品的查詢。那麼,如果需要有條件的計數,有什麼意義呢:counter_cache? – 2010-02-26 01:12:44

+0

它以'N + 1'結尾。 – 2010-02-26 01:13:40

+0

正如我在我的回答中所提到的,如果他主要是在沒有條件的情況下計算銷售額,他應該選擇counter_cache。我已經修改了我的答案,以解決條件情形下的N + 1問題。這又取決於他的用例。 – 2010-02-26 01:27:50

2

這不是一個答案,而是三個答案比較這個問題。我已經給出了我的答案。在的:joins:include的工作中存在混淆。所以我花了一些時間分析三個解決方案的SQL日誌。

方法1:獲取使用count

#To return the sales count for each product 
    ps_count_hash = Product.count(:joins => [:sales], :group => "products.id") # sql 1.1 

    # To print the product id and sales count 
    ps_count_hash.each do | product_id, sales_count| 
    p "Product#{product_id} - (#{sales_count})" 
    end 

    # To print the product details and sales count 
    # get all the products associated 
    Product.find_all_by_id(ps_count_hash.keys).each |product| #sql 1.2 
    p "Product[id = #{product.id}, name = #{product.name}] - (#{ps_count_hash[product.id]})" 
    end 

方法2的銷售數:通過join

Product.find(:all, :joins=>[:sales]).each do |product| #sql 2.1 
    p "Product[id = #{product.id}, name = #{product.name}] - (#{product.sales.size})" # sql 2.2 - 2.(2+N) 
    end 

方法3獲得的產品:通過include獲取產品

Product.find(:all, :include=>[:sales]).each do |product| #sql 3.1 and 3.2 
    p "Product[id = #{product.id}, name = #{product.name}] - (#{product.sales.size})" 
    end 

現在讓我們來看看這三個方法生成的SQL語句

SQL語句的方法1 - 2 SQL

SELECT count(*) AS count_all, products.id AS products_id FROM `products` INNER JOIN `sales` ON sales.product_id = products.id GROUP BY products.id 
SELECT `products`.* FROM `products` WHERE (`products`.`id` IN (1,2)) 

SQL語句的方法2 - 2 SQL

SELECT * FROM `products` 
SELECT `sales`.* FROM `sales` WHERE (`sales`.product_id IN (1,2)) 

方法3的SQL語句 - N + 1 SQL

SELECT `products`.* FROM `products` INNER JOIN `sales` ON sales.product_id = products.id 
SELECT * FROM `sales` WHERE (`sales`.product_id = 1) 
SELECT * FROM `sales` WHERE (`sales`.product_id = 2) 

最佳由銷售計數產物(與出銷售信息)的方法:方法1

最佳由銷售計數產物(與銷售信息)的方法:方法2

方法3具有N + 1問題。所以這是沒有爭議的。