将表列添加到Group by子句 - Ruby on Rails - Postgresql

时间:2010-04-23 15:15:04

标签: ruby-on-rails ruby postgresql heroku

我正在尝试使用Heroku,显然Postgresql比聚合函数的SQL要严格得多。当我向Heroku推送时,我收到一个错误,说明如下。

On another question我问我收到了一些指导,说我应该将列添加到我的group by子句中,我不知道该怎么做。请参阅下面的完整错误和PostsControll#index。

SELECT posts.*, count(*) as vote_total FROM "posts"   INNER JOIN "votes" ON votes.post_id = posts.id   GROUP BY votes.post_id ORDER BY created_at DESC LIMIT 5 OFFSET 0):

PostsController

def index
    @tag_counts = Tag.count(:group => :tag_name, 
       :order => 'count_all DESC', :limit => 20)
       conditions, joins = {}, :votes

    @ugtag_counts = Ugtag.count(:group => :ugctag_name, 
       :order => 'count_all DESC', :limit => 20)
       conditions, joins = {}, :votes

    @vote_counts = Vote.count(:group => :post_title, 
          :order => 'count_all DESC', :limit => 20)
          conditions, joins = {}, :votes


       unless(params[:tag_name] || "").empty?
         conditions = ["tags.tag_name = ? ", params[:tag_name]]
         joins = [:tags, :votes]
       end
       @posts=Post.paginate(
                 :select => "posts.*, count(*) as vote_total", 
                 :joins => joins, 
                 :conditions=> conditions, 
                 :group => "votes.post_id", 
                 :order => "created_at DESC",
                 :page => params[:page], :per_page => 5)
        @popular_posts=Post.paginate(
                 :select => "posts.*, count(*) as vote_total", 
                 :joins => joins, 
                 :conditions=> conditions, 
                 :group => "votes.post_id", 
                 :order => "vote_total DESC",
                 :page => params[:page], :per_page => 3)

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @posts }
      format.json { render :json => @posts }
      format.atom
    end
  end

4 个答案:

答案 0 :(得分:3)

MySQL和SQLite非常灵活,它们允许select-list中的列而不在GROUP BY子句中命名(而不是在COUNT()之类的聚合函数中)。但是这种灵活性会导致模糊的查询。

PostgreSQL仅与ANSI SQL标准一样严格。我在这个问题上测试过的所有其他数据库(Oracle,Microsoft,IBM DB2,Firebird)就像PostgreSQL一样。

您需要做的是使您的选择列表中的posts列列表与您的GROUP BY子句中指定的列相匹配。通过选择较少的列或向:group添加列来执行此操作。

我不是Rails专家,我找不到如何将多列作为参数传递给:group的示例。查看active_record / base.rb的代码,它似乎只是将选项值复制到文字SQL GROUP BY子句中。所以我想(没有尝试过)你可以这样做:

...
:group => "posts.post_id, posts.foo, posts.bar, posts.baz", 
...

请注意,您必须在选择列表中将列的每个列命名为不属于聚合函数的列。

答案 1 :(得分:1)

您是否尝试过切换到使用posts.id,例如:

SELECT posts.*, count(*) as vote_total FROM "posts"   INNER JOIN "votes" ON votes.post_id = posts.id   GROUP BY posts.id ORDER BY created_at DESC LIMIT 5 OFFSET 0);

如果是这样,那么结果集与现有SQL的结果有何不同?

答案 2 :(得分:1)

我提出了一个变体,它涉及动态而不是在编译时创建表名连接恶魔

  

model_columns = Model.column_names.collect do | c | “#{Model.table_name}。#{c}”end.join(“,”)

答案 3 :(得分:0)

我刚刚在Rails 3上处理这个问题。这是我提出的解决方案:

group_models = [Post, Vote, etc.]
group_columns = ['"votes"."post_id"'] # whatever column you were specifying with :group 
group_models.each do |model|
  group_columns += model.column_names.map { |col| "\"#{model.table_name}\".\"#{col.name}\"" }
end
Post.where(whatever...).group(group_columns)