3

我想不出更好的方法来重构下面的代码(参见这个问题),尽管我知道它非常难看。但是,它会引发 Postgres 错误(不是 SQLite):

ActiveRecord::StatementInvalid: 
PG::Error: ERROR:  
column "articles.id" must appear in the GROUP BY clause or be used in an aggregate function

查询本身是:

SELECT "articles".* 
FROM "articles" 
WHERE "articles"."user_id" = 1 
GROUP BY publication

这来自以下视图代码:

=@user.articles.group(:publication).map do |p|
  =p.publication
  =@user.articles.where("publication = ?", p.publication).sum(:twitter_count)
  =@user.articles.where("publication = ?", p.publication).sum(:facebook_count)
  =@user.articles.where("publication = ?", p.publication).sum(:linkedin_count)

在 SQLite 中,这给出了输出(例如)NYT 12 18 14 BBC 45 46 47 CNN 75 54 78,这正是我所需要的。

如何改进代码以消除此错误?

4

2 回答 2

7

使用时,GROUP BY您不能使用SELECT既不是聚合函数的一部分也不是GROUP BY聚合函数中使用的字段。这是由 SQL 标准指定的,尽管某些数据库还是选择执行此类查询。由于没有单一的正确方法来执行这样的查询,他们倾向于只选择他们找到的第一行并返回它,因此结果会发生不可预测的变化。

看起来你想说:

“对于每个出版物,让我获得该出版物的 twitter、facebook 和linkedin 计数的总和”。

如果是这样,你可以写:

SELECT publication,
       sum(twitter_count) AS twitter_sum,
       sum(linkedin_count) AS linkedin_sum,
       sum(facebook_count) AS facebook_sum
FROM "articles" 
WHERE "articles"."user_id" = 1 
GROUP BY publication;

将其转换为 ActiveRecord/Rails ...由您决定,我不使用它。看起来这几乎是您尝试编写的内容,但 ActiveRecord 似乎正在修改它,也许试图在本地执行总和。

于 2013-05-07T12:23:40.443 回答
5

克雷格的回答很好地解释了这个问题。Active Recordselect *默认会使用,但您可以轻松地覆盖它:

@user.articles.select("publication, sum(twitter_count) as twitter_count").group(:publication).each do |row|
  p row.publication # "BBC"
  p row.twitter_count # 45
end
于 2013-05-07T13:03:14.463 回答