Rails: 必须出现在 GROUP BY 子句中或者被用在聚合函数中。

8

我正在跟随Bate的图形和图表教程,但是出现了一个错误。

这是我得到的错误消息:

PG::Error: ERROR:  column "orders.created_at" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT created_at, sum(amount) as total_amount FROM "orders"...
               ^
: SELECT created_at, sum(amount) as total_amount FROM "orders"  WHERE ("orders"."created_at" BETW

这是我在我的订单模型中使用的代码

def self.total_grouped_by_day(start)
    orders = where(created_at: start.beginning_of_day..Time.zone.now)#.all
    orders = orders.group("date(created_at)")
    orders = orders.select("date(created_at), sum(amount) as total_amount")
    orders.group_by { |order| order.created_at.to_date }
  end

在我的帮助器中

def orders_chart_data
        orders_by_day = Order.total_grouped_by_day(3.weeks.ago)
        (3.weeks.ago.to_date..Date.today).map do |date|
            {
                created_at: date,
                price: orders_by_day[date].first.try(:total_amount) || 0
            }
        end
    end

我正在使用Postgres(我相信这是导致此错误的主要原因)和Rails 3.x.x。

为什么会出现这个错误,我该如何解决?

提前感谢您的帮助。


在我的控制台中:

Order.where(created_at: 3.weeks.ago.beginning_of_day..Time.zone.now).group("date(created_at)").select("date(created_at), sum(amount) as total_amount")

& 这就是我得到的内容

[#<Order >, #<Order >, #<Order >] 

很奇怪...


在我的控制台中运行以下代码:Order.where(created_at: 3周前的开始时间..Time.zone.now).group("date(created_at)").select("date(created_at), sum(amount) as total_amount").first.total_amount

会得到这个结果:

Creating scope :page. Overwriting existing method Order.page.
  Order Load (18.1ms)  SELECT date(created_at), sum(amount) as total_amount FROM "orders" WHERE ("orders"."created_at" BETWEEN '2013-07-05 00:00:00.000000' AND '2013-07-26 23:50:38.876609') GROUP BY date(created_at) LIMIT 1
 => "10.0"
2个回答

6
您正在按date(created_at)分组,但未选择该列。请更改此行:
orders = orders.select("created_at, sum(amount) as total_amount")

转换为:

orders = orders.select("date(created_at), sum(amount) as total_amount")

这也会导致下一行的group_by发生变化。

从我的一个项目中,使用略有不同的属性,但与您执行的操作相同:

1.9.3p327 > User.group('date(created_at)').select('date(created_at), sum(id) as total_amount').first.attributes
  User Load (1.2ms)  SELECT date(created_at), sum(id) as total_amount FROM "users" GROUP BY date(created_at) LIMIT 1
 => {"date"=>"2011-09-27", "total_amount"=>"657"}

在下一个 group_by 行中遇到了那个错误吗?你会的。在进行 group_by 操作之前,你需要查看返回的内容,因为它可能不再是你想要的。记住,你调整了 select 语句,所以该列不再被返回。 - Philip Hallstrom
不要这样调用。在调用之前,你应该先查看返回值。我记得返回的是一个哈希表。键将是“date(created_at)”字符串值,而值将是其余参数。但我可能完全错了。请查看文档并在控制台中尝试。 - Philip Hallstrom
请看我的问题。我编辑了我在控制台中得到的内容。我做错了什么? - goo
你需要检查这些单独的对象。如果你运行并追加“.first.total_amount”会发生什么?你可能需要更改你的选择以返回命名列,这样你就可以像使用“sum(amount) as total_amount”一样引用它们。 - Philip Hallstrom
好的,我现在明白了。我使用的是“created_at”而不是“date”,但现在这只是导致了另一个问题 叹气... 我在我的helper中得到了“undefined method 'first' for nil:NilClass”错误(请参见问题)。 - goo
显示剩余4条评论

0
一个转换为日期的方法会是最简单和最快的。这里是原始的 SQL,因为我不是 Ruby 语法的专家(也不喜欢 ORM 在一般情况下破坏 SQL)。
SELECT created_at::date, sum(amount) AS total_amount
FROM   orders
WHERE  created_at ...
GROUP  BY created_at::date
ORDER  BY created_at::date

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接