2012-12-18 49 views
4

我用這個方法:軌道/ Postgres的:「必須出現在GROUP BY子句或聚合函數中使用」

def self.lines_price_report(n) 
    Income.group('date(filled_at)').having("date(filled_at) > ?", Date.today - n).sum(:lines_price) 
    end 

我得到這個錯誤的Heroku:

PG::Error: ERROR: column "incomes.filled_at" must appear in the GROUP BY clause 
or be used in an aggregate function 

我該如何解決這個問題?謝謝。

執行的查詢:

SELECT SUM("incomes"."lines_price") AS sum_lines_price, date(filled_at) 
AS date_filled_at FROM "incomes" 
HAVING (date(filled_at) > '2012-12-04') 
GROUP BY date(filled_at) ORDER BY filled_at ASC 

預期結果

[["2012-12-04", SUM_FOR_DATE], ["2012-12-05", SUM_FOR_DATE], ...] 

回答

6

你的錯誤是在默認範圍內按順序使用filled_at。

您可以修復它使用未範圍消除默認範圍:

Income.unscoped 
.group('date(filled_at)') 
.having("date(filled_at) > ?", Date.today - n) 
.sum(:lines_price) 

Income.unscoped 
    .group('date(filled_at)') 
    .having("date(filled_at) > ?", Date.today - n) 
    .sum(:lines_price) 
    .order('date(filled_at) ASC') 

但我認爲,更好的將是使用的地方,而不必

Income.unscoped 
    .where("date(filled_at) > TIMESTAMP ?", Date.today - n) 
    .group('date(filled_at)') 
    .sum(:lines_price) 
    .order('date(filled_at) ASC') 

SQLFiddle

你必須小心使用TIMESTAMP,因爲2012-12-04將變成2012-12-04 00:00:00所以如果你不想在這一天結果使用Date.today - (n - 1)

如果在filled_at列上創建索引

create index incomes_filled_at on incomes(filled_at); 

遷移:

add_index :incomes, :filled_at 

,你必須在這個表中的索引很多數據將在過濾中使用。所以查詢應該快得多。因此,只需編寫兩者並測試哪個更快(如果沒有,您必須在filled_at上創建索引)。

+0

'reorder'可以用來覆蓋默認排序。 'unscoped'將放棄所有默認範圍,這可能是不希望的。使用'where'而不是'having'是個好主意。 – khustochka

+0

@khustochka如果您有多個默認範圍,我同意'reorder'而不是'unsocped'。我改變了答案。 – sufleR

+0

謝謝你的作品。爲什麼在哪裏比擁有更好?我如何在filled_at上創建一個索引? – Alex

3

我想這是因爲你在GROUP BY使用date(filled_at)只是filled at秩序。正如我猜想順序是從默認的範圍,你需要覆蓋reorder。我會建議:

Income.sum(:lines_price). 
    group('date(filled_at)'). 
    having("date(filled_at) > ?", Date.today - n). 
    reorder("date(filled_at) ASC") 
+0

我同意。 ORDER BY將'filled_at'(不含日期位)添加爲隱式SELECT,該SELECT必須出現在GROUP BY中。 –

+0

它的工作,但奇怪的是,我沒有使用select_rows得到一個數組數組?我在rails console中運行它:'Income.group('date(filled_at)')。having(「date(filled_at)>?」,Date.today - n).reorder(「date(filled_at)ASC」)。sum (:lines_price).to_a'並得到了[[「2012-12-05」,#],...]]]'。 – Alex

+0

你是對的,這是'sum'和其他AR聚合是如何工作的(它實際上返回一個散列,其中的鍵作爲列進行分組,並將值作爲sum,'to_a'將其轉換爲數組數組)。感謝您糾正我,我刪除了更長的解釋,因爲它是不正確的。 – khustochka

1

當你想在PostgreSQL上使用分組時,應該在分組上使用select選項。

Income.select('filled_at').group('date(filled_at)').having("date(filled_at) > ?", Date.today - n).sum(:lines_price) 
相關問題