Rails关联查询中如何使用limit限制结果数量

6

我正在尝试查询所有的站点,并关联测量数据

但是我只想要最近的一次测量(按created_at降序排序),因为一个站点可能有成千上万条测量数据。

我已经尝试过

Station.joins(:measures).limit(1) 

但这只是限制了站点。
附加信息:
站点有许多措施。
措施属于站点。
我已经阅读了Active Records docs,但只有有关在关联上使用where条件的信息。
该应用程序仅针对Postgres,接受SQL。

编辑:添加了 schema.rb 的例外部分:

  create_table "measures", force: true do |t|
    t.integer  "station_id"
    t.float    "speed"
    t.float    "direction"
    t.float    "max_wind_speed"
    t.float    "min_wind_speed"
    t.float    "temperature"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.float    "speed_calibration"
  end

  add_index "observations", ["created_at"], name: "index_observations_on_created_at", using: :btree
  add_index "observations", ["station_id"], name: "index_observations_on_station_id", using: :btree

  create_table "stations", force: true do |t|
    t.string   "name"
    t.string   "hw_id"
    t.float    "latitude"
    t.float    "longitude"
    t.float    "balance"
    t.boolean  "offline"
    t.string   "timezone"
    t.integer  "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "slug"
    t.boolean  "show",                         default: true
    t.float    "speed_calibration",            default: 1.0
    t.datetime "last_observation_received_at"
  end

加法 这是当前使用的非常粗糙的代码:

def all_with_latest_measure
  if user_signed_in? && current_user.has_role?(:admin)
    stations = Station.all.load
  end
  stations ||= Station.where(show: true).load

  if stations.size
    ids = stations.map { |s| s.id }.join(',')
    where = "WHERE m.station_id IN(#{ids})" unless ids.empty?
    measures = Measure.find_by_sql(%Q{
    SELECT DISTINCT ON(m.station_id, m.created_at)
      m.*
    FROM measures m
    #{where}
    ORDER BY m.created_at DESC
    })

    stations.each do |station|
      # Setup has_many relationship between station and Measure
      # Prevents n+1 queries
      measure = Measures.find { |m| m.station_id == station.id  }
      if measure
        measure.station = station
        station.latest_measure = measure
      end
    end
  end
end

请展示两个表的结构。另外,“最近的措施”如何定义? - Raj
你想要每个单独站点的最新措施吗? - Raj
是的,它们显示在地图上。http://www.blast.nu/ - max
你想如何限制最近的度量?是基于度量创建时间(created_at)吗? - Raj
@emaillenin 是的,通过 created_at。 - max
3个回答

7

我相信在Rails 4中,您可以对关联应用作用域:

class Stations
  has_many :measures, -> { order('created_at DESC').limit(1)  }
end

然后:

2.0.0-p353 :008 > Station.first.measures
  Station Load (0.1ms)  SELECT "stations".* FROM "stations" ORDER BY "stations"."id" ASC LIMIT 1
  Measure Load (0.1ms)  SELECT "measures".* FROM "measures" WHERE "measures"."station_id" = ? ORDER BY created_at DESC LIMIT 1  [["station_id", 1]]
< p > 编辑:如果你只需要最近的一个,你可以使用has_one。它适用于Rails 4和Rails 3,语法稍有改动:

class Stations
  has_one :recent_measure, -> { order('created_at DESC')  }, class_name: 'Measure' # Rails 4
  has_one :recent_measure, order: 'created_at DESC', class_name: 'Measure' # Rails 3
end

1
问题在于这将会为每个站点触发一个查询,这几乎不是最优的。 - max
Station.includes(:recent_measure) 只发起两个查询,一个用于获取站点信息,另一个用于获取测量数据。但是,在这种情况下,所有 测量数据都会被获取并在客户端被丢弃。如果有大量的数据,则可能会对性能产生影响。 - Kombajn zbożowy
2
这里描述了一个针对Rails 4和Postgres的类似情况的技巧,以避免加载所有子记录。我没有在我的机器上验证,但你可以看一下。 - Kombajn zbożowy
1
谢谢提供链接,看起来正是我所需要的。 - max
哇,这证明对我一直努力解决的问题非常有帮助。谢谢! - Christoffer

0
如果计量数据在特定时间间隔内获取,则应通过created_at字段过滤度量值。

它们每隔5分钟进行一次采集,但是进程的“计时器”是在气象站开启时开始计时的。因此它们不会同时发送数据。 - max

0

Station.joins(:measures).group(:station_id).order("measures.created_at DESC")

Station.joins(:measures).group(:station_id).order("measures.created_at DESC")


1
PG::Error: ERROR: column "stations.id" must appear in the GROUP BY clause or be used in an aggregate function - max

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接