【问题标题】:grouping with a non-primary key in postgres / activerecord在 postgres / activerecord 中使用非主键分组
【发布时间】:2013-08-27 13:16:08
【问题描述】:

我有一个模型 Lap:

class Lap < ActiveRecord::Base
  belongs_to :car

  def self.by_carmodel(carmodel)
    scoped = joins(:car_model).where(:car_models => {:name => carmodel})
    scoped
  end

  def self.fastest_per_car
    scoped = select("laps.id",:car_id, :time, :mph).group("laps.id", :car_id, :time, :mph).order("time").limit(1)
    scoped
  end
end

我只想返回每辆车的最快圈速。

所以,我需要按 Lap.car_id 对圈数进行分组,然后只返回基于该车的最快圈速,这将由 Lap.time 列确定

基本上我想将我的方法堆叠在我的控制器中:

@corvettes = Lap.by_carmodel("Corvette").fastest_per_car

希望这是有道理的......

当尝试仅运行 Lap.fastest_per_car 时,我将所有内容限制为 1 个结果,而不是每辆车 1 个结果。

我必须做的另一件事是添加“laps.id”,因为 :id 在我的结果中也显示为空。如果我只是选择(:id)它说的是模棱两可

【问题讨论】:

  • 如果汽车有多圈共享最快时间,您会如何处理?返回所有的,最早的,最近的.. ?
  • 在这种情况下,我可能只想返回最新的。这些时间将类似于 54.354,因此完全相同的机会很小,但可能存在。基本上只返回一个所说的 car_id 最快的时间

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


【解决方案1】:

我认为一个不错的方法是添加一个基于有效 SQL 语法的 where 子句,以返回最快的单圈。

类似这样的相关子查询...

select ...
from   laps
where  id = (select   id
             from     laps laps_inner
             where    laps_inner.car_id = laps.car_id
             order by time asc,
                      created_at desc
             limit    1)

这有点复杂,因为需要在 created_at 上进行抢七。

rails 范围只是:

where("laps.id = (select   id
                  from     laps laps_inner
                  where    laps_inner.car_id = laps.car_id
                  order by time asc,
                           created_at desc
                  limit    1)")

car_id 上的索引非常重要,如果这是 (car_id, time asc) 上的复合索引,那就更好了。

【讨论】:

  • 谁能告诉我如何用activerecord而不是sql来写?只是因为我很好奇
  • 对于纯 activerecord 方法,您可能需要两个步骤:一个选择每个 car_id 及其各自的最短时间,然后另一个步骤接受此列表作为检索完整记录的一组条件.不过,这并不能解决最终结果集中的唯一性——我不确定如果不求助于某种 SQL 覆盖,您将如何处理它。
  • 在下面查看我的答案,看看是否可以。看起来更简单。
  • 我对 DISTINCT ON () 并不熟悉——我认为它不是 ANSI 标准 SQL——但它看起来是一个很棒的方法。我很想看看大型数据集的性能有多好,但我不明白为什么它不应该很好。不错。
【解决方案2】:

您正在使用limit,它将返回一个值。每辆车没有一个价值。要每圈返回一辆车的价值,您只需加入表格并按一组列分组,这些列将标识一圈(id 是最简单的)。

此外,您可以对 ActiveRecord 更友好:

class Lap < ActiveRecord::Base
  belongs_to :car

  def self.by_carmodel(carmodel)
    joins(:car_model).where(:car_models => {:name => carmodel})
  end

  def self.fastest_per_car
    joins(:car_model)
      .select("laps.*, MIN(car_models.time) AS min_time")
      .group("laps.id")
      .order("min_time ASC")
  end
end

【讨论】:

  • 我认为我不需要加入 car_model。因为我不需要基于 car_model 的最快单车,而是最快圈速列表。如果有意义的话,只有每个唯一的 :car_id 最快的一圈。所以基本上我需要得到所有的圈数并按时间 ASC 排序,然后按 car_id 分组,并且只为每组 car_id 的圈数提取分钟(时间)
  • 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
  • create_table "cars", force: true do |t| t.text "mods" t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" t.integer "car_make_id" t.integer "car_model_id" t.integer "engine_id" t.integer "transmission_id" t.字符串“昵称”结束
  • 我不能只用两个查询来做到这一点吗?获取每个唯一 car_id 的所有最快圈数的 ID ** ids = Lap.order("time").select("DISTINCT car_id") ** then ** Lap.where(:id => ids).order("时间") **
【解决方案3】:

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

在我的模型中:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-04
    • 1970-01-01
    • 2021-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-12
    相关资源
    最近更新 更多