带有postgres的Rails - activerecord查询:按列ASC排序,然后按列分类

时间:2013-09-03 15:12:42

标签: sql ruby-on-rails ruby-on-rails-3 postgresql activerecord

我有一个模型Laps,属于:Car

所以每辆车都有很多圈,但是我需要进行一次查询,我可以在前10名中获得最快的成绩,但每辆车只能获得最快圈速。换句话说,我不希望一辆车被允许在列表中排名前十的最快圈数中的5个。

每一圈都有一个:car_id字段。我想在这个专栏上分组,然后从分组中用min(Lap.time)行拉出一圈。 (AKA从那个独特的最快单圈时间:car_id)。

所以对于Postgres,我最好的方法是什么?我可以先订购所有圈数,然后再分组,然后从组中拉出第一圈。或者分组不保留排序顺序?

以下是Lap模型的架构:

  create_table "laps", force: true do |t|
    t.decimal  "time"
    t.string   "video_url"
    t.integer  "car_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "user_id"
    t.boolean  "approved"
  end

我是否必须使用两个组合查询?

通过这样做,我可以得到一个独特的汽车id:

select('DISTINCT ON (car_id) *')

但是,我需要订购圈数,以便获得每个car_id的最小值(lap.time)。所以,当我抛出这样的订单时:

select('DISTINCT ON (car_id) *').order('car_id, time ASC').sort_by! {|ts| ts.time}

这很有效,但似乎是一种奇怪的方式。一旦我尝试更改订单,比如从订单中删除car_id,我就会收到postgres错误。

2 个答案:

答案 0 :(得分:4)

正如您所发现的那样,DISTINCT ON在这里不适合您,因为它与您想要排序的第一个术语(时间)不匹配。您需要使用GROUP BY:

Lap.group(:car_id).limit(10).minimum(:time)

或者,您可以创建一个窗口子查询 - 但构建起来相当麻烦。如果你需要一段时间的实际圈数信息,你可能必须走那条路:

subquery = Lap.select('lap.*, ROW_NUMBER() OVER ( PARTITION BY car_id ORDER BY time, car_id ) as rowNum').to_sql
Lap.scoped.from(Arel.sql("(#{subquery}) fast_laps"))
   .where('rowNum = 1')
   .order('time, car_id')
   .limit(10)

答案 1 :(得分:0)

这就是我所做的和它的工作。如果有更好的方法可以解决这些问题,请发表您的答案:

在我的模型中:

def self.fastest_per_car
    select('DISTINCT ON (car_id) *').order('car_id, time ASC').sort_by! {|ts| ts.time}
end