【问题标题】:order by column in a group by aggregate function按聚合函数在组中按列排序
【发布时间】:2017-10-18 21:06:09
【问题描述】:

我有场地,场地有很多物品。我想获得每个场地的最新项目。要在单个查询中返回每个场地的最新项目,我可以将 max 聚合函数与 group by 一起使用:

class Item < ActiveRecord::Base
  scope :newest_items, -> (venue_ids) {
    select('max(items.id) as id, venue_id').joins(:venue).where('venues.id IN (?)', venue_ids).group(:venue_id)
  }
end

生成的sql:

Item.newest_items(Venue.pluck(:id)).to_sql 
 => "SELECT max(items.id) as id, venue_id FROM \"items\" 
    INNER JOIN \"venues\" ON \"venues\".\"id\" = \"items\".\"venue_id\" 
    WHERE (venues.id IN (1,2,3)) 
    GROUP BY \"items\".\"venue_id\"" 

所以我有每个场地的最新项目。但是有了这个结果,我想按创建日期对结果进行排序。

如果我有一个 order by 子句:

 class Item < ActiveRecord::Base
  scope :newest_items, -> (venue_ids) {
    select('max(items.id) as id, venue_id')
    .joins(:venue).where('venues.id IN (?)', venue_ids)
    .group(:venue_id).order('items.created_at desc')
  }
end

sql:

Item.close_by_items(Venue.pluck(:id)).to_sql
=> "SELECT max(items.id) as id, venue_id 
    FROM \"items\" INNER JOIN \"venues\" ON \"venues\".\"id\" = \"items\".\"venue_id\" 
    WHERE (venues.id IN (1,2,3)) 
    GROUP BY \"items\".\"venue_id\" 
    ORDER BY items.created_at desc" 

我得到错误:

ActiveRecord::StatementInvalid: PG::GroupingError: ERROR: column "items.created_at" 必须出现在 GROUP BY 子句中或用于聚合函数中

为什么 created_at 必须出现在 GROUP BY 子句中或在聚合函数中使用?我要做的就是按 created_at 时间对 group by 子句的结果进行排序。

【问题讨论】:

  • 你不能order by一个未被选中的列。
  • @vkp 即使添加到select语句中,错误也是一样的:列“items.created_at”必须出现在GROUP BY子句中或者在聚合函数中使用
  • 是的..如果你不聚合它,它应该在group by
  • @vkp 为什么?我不明白为什么在 group by 中需要它。我正在尝试对分组结果进行排序。

标签: sql ruby-on-rails postgresql


【解决方案1】:

实现此目的的另一种方法是将ORDER 放入子查询中,这样的事情应该可以按您的预期工作:

Item.close_by_items(Venue.pluck(:id)).to_sql
=> "SELECT max(sub.id) as id, sub.venue_id FROM (SELECT items.id, venue_id 
    FROM \"items\" INNER JOIN \"venues\" ON \"venues\".\"id\" = \"items\".\"venue_id\" 
    WHERE (venues.id IN (1,2,3)) ORDER BY items.created_at desc") AS sub
    GROUP BY \"sub\".\"venue_id\" 

【讨论】:

    【解决方案2】:

    如果您只希望每个场地只有一个项目,可以使用DISTINCT ON 而不是分组。

    class Item < ActiveRecord::Base
      def self.newest_items(*venues)
        select('DISTINCT ON(items.venue_id), items.*')
          .where(venue_id: *venues)
          .order('items.created_at DESC')
      end
    end
    

    【讨论】:

    • 当我在我的问题中说“最新项目”时,我的意思是具有最大 id 的项目。 DISTINCT ON 类似于 MAX?
    • 不,不是。 DISTINCT ON(items.venue_id) 表示每个items.venue_id 将只返回一行查询。我真的不明白为什么你如此坚持使用 id 进行排序。无论如何,按items.created_at 排序将为您提供最后创建的行。
    猜你喜欢
    • 2013-02-03
    • 1970-01-01
    • 2014-11-17
    • 1970-01-01
    • 1970-01-01
    • 2019-03-09
    • 2021-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多