【问题标题】:Rails with postgres - activerecord query: sort by column ASC, then group by column带有 postgres 的 Rails - activerecord 查询:按列 ASC 排序,然后按列分组
【发布时间】:2013-09-03 15:12:42
【问题描述】:

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

所以每辆车都有很多圈,但我需要做一个查询,我在哪里拉入前 10 最快圈,但只拉入每辆车的最快圈。换句话说,我不希望一辆汽车被允许在列表中的前 10 圈中跑 5 圈。

每一圈都有一个 :car_id 字段。我想在此列上分组,然后从分组中拉出带有 min(Lap.time) 行的圈。 (也就是那个独特的 :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

我必须为此使用两个组合查询吗?

我可以通过这样做获得一个独特的车牌圈数:

select('DISTINCT ON (car_id) *')

但是,我需要订购圈数,以便它获得每个 car_id 的 min(lap.time)。因此,当我发出这样的订单时:

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

这可行,但似乎是一种奇怪的方法。一旦我尝试更改订单,例如从订单中删除 car_id,我就会收到 postgres 错误。

【问题讨论】:

  • 也许是这个? Lap.select('min(laps.time), laps.*').group('laps.id, laps.car_id').limit(10)
  • 我刚试过这个。它给了我来自同一个 car_id 的多圈
  • 这就是我所做的,它正在努力以最快的速度获得唯一的 car_id,但是当我调试对象时,它似乎没有按时间排序。如果有更好的方法来解决这些问题,请发布您的答案:select('DISTINCT ON (car_id) *').order('car_id, time ASC')

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


【解决方案1】:

正如您所发现的那样,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)

【讨论】:

  • 我已经尝试过了,但我得到:PG::InvalidColumnReference: ERROR: SELECT DISTINCT ON 表达式必须匹配初始 ORDER BY 表达式。我还需要更改我的选择顺序吗?
  • @JoelGrannas,对 - 这行不通。更新了替代答案。
  • 我确实需要圈速信息(时间、id、mph、video_url)。您能否详细说明窗口子查询或提供该方法
  • 与我下面的答案相比,您认为子查询的性能如何?
  • @JoelGrannas - 对于少量数据,这并不重要。对于大量数据(数百行),子查询的性能会显着提高,因为它不需要传输要在 ActiveRecord 中创建和排序的每条car_id 记录。对于更大的数量,窗口化也会稍微减慢 - 您可能需要更改子查询以使用 GROUP BY 来使用索引,并使用外部查询来获取整行数据。但这可能不会在您接近 100 万行标记之前发生。
【解决方案2】:

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

在我的模型中:

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

【讨论】:

  • 我会远离 sort_by!并让 ruby​​ 完成您的数据库应该处理的繁重工作
猜你喜欢
  • 2016-05-10
  • 1970-01-01
  • 2023-04-06
  • 2020-11-11
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
  • 2018-08-22
相关资源
最近更新 更多