2012-12-18 48 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) 
相关问题