2016-08-31 12 views
1

我有一個模型CompanyCompany有很多DailyData。 而DailyData有列volumedate如何使用帶有來自Rails的order子句的postgres AVG()函數

要計算的最近10個工作日內,我寫像平均體積:

class Array 
    def sum 
    inject(0) { |result, el| result + el } 
    end 

    def mean 
    sum.to_d/size 
    end 
end 

company = Company.first 
company.daily_data.order(date: :desc).limit(10).pluck(:volume).mean 

此代碼工作正常,但我想使用的Postgres AVG()功能。

company.daily_data.select('AVG(volume) as average_volume').order(date: :desc) 

該代碼最終出現錯誤:

PG::GroupingError: ERROR: column "daily_data.date" must appear in the GROUP BY clause or be used in an aggregate function 

,但如果我在方法鏈把.group(:date),將SQL reurns多個結果。 如何使用postgresql AVG()函數獲取最近10卷的平均值?

回答

2

一個ActiveRecord這樣的查詢:

company.daily_data.select('AVG(volume) as average_volume').order(date: :desc) 

並沒有真正太大的意義。 avg是SQL中的一個聚合函數,因此它需要對一組行進行操作。但是,您並沒有告訴數據庫如何對行進行分組,而是告訴數據庫計算整個表的平均音量,然後通過最終結果集中不存在的內容對該單個值進行排序。

投擲limit在:

company.daily_data 
     .select('AVG(volume) as average_volume') 
     .order(date: :desc) 
     .limit(10) 

也不會有幫助,因爲limit之後的order應用而且那時你已經用你的企圖混淆avg(volume)數據庫。

我可能會使用派生表,如果我在SQL這樣做,是這樣的:

select avg(volume) as average_volume 
from (
    select volume 
    from where_ever... 
    where what_ever... 
    order by date desc 
    limit 10 
) dt 

FROM子句中的派生表中尋找您想要的volume秒,然後整個查詢平均值那些10 volume s。

或者,你可以使用子查詢來攫取利益的行:

select avg(volume) as average_volume 
from where_ever... 
where id in (
    select id 
    from where_ever... 
    where what_ever... 
    order by date desc 
    limit 10 
) 

子查詢的方法是相當簡單的用ActiveRecord,像這樣來實現:

ten_most_recent = company.daily_data.select(:id).order(:date => :desc).limit(10) 
company.daily_data.where(:id => ten_most_recent).average(:volume) 

如果您在第二行的末尾拋出一個to_sql調用,您應該看到類似於子查詢SQL的內容。

你也可以使派生表的方法與ActiveRecord一起工作,但它有點不太自然。有一個from method在ActiveRecord的,將採取一個ActiveRecord查詢打造from (select ...)派生表結構,但你要務必手動命名派生表:

ten_most_recent = company.daily_data.select(:volume).order(:date => :desc).limit(10) 
AnyModelAtAll.from(ten_most_recent, 'dt').average('dt.volume') 

你必須使用一個字符串參數average幷包含前綴dt.以防止ActiveRecord嘗試添加其自己的表名稱。

當然,你會將所有這些東西隱藏在某個方法中,這樣你就可以隱藏細節。也許在daily_data關聯的擴展方法:

has_many :daily_data, ... do 
    def average_volume(days) 
    #... 
    end 
end 

,這樣你可以說這樣的話:

company.daily_data.average_volume(11) 
相關問題