使用ActiveRecord Rails优化多个查询

时间:2017-10-30 12:10:22

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

我有一种目前正常运行的方法,但我想知道是否有更好的方法。

有一个项目(歌曲)队列,其中索引增加并相应地更新。

这是我目前的代码:

def self.get_next_schedule_track(schedule)
  begin
    # get current position
    current_position = schedule.current_position
    current_schedule_track = scheduled_track_at_position(schedule, current_position)

    # check if next position is valid, otherwise pick random track
    next_position = current_position + 1
    next_schedule_track = scheduled_track_at_position(schedule, next_position)

    if next_schedule_track.nil?
      random_track  = Track.where.not(id: current_schedule_track.track.id).order("RANDOM()").first
      ScheduleTrack.create(track: random_track, schedule: schedule)
      next_schedule_track = scheduled_track_at_position(schedule, next_position)
    end

    # advance current_position
    schedule.current_position = next_position
    schedule.save

    # update state of schedule tracks
    current_schedule_track.update(state: 'playing') unless current_schedule_track.nil?
    next_schedule_track.update(state: 'next') unless next_schedule_track.nil?

    # if previous track set state to played
    previous_schedule_track = scheduled_track_at_position(schedule, current_position - 1)
    previous_schedule_track.update(state: 'played') unless previous_schedule_track.nil?

    # broadcast new current position
    ActionCable.server.broadcast "schedule_channel", { title: 'next position', body: next_schedule_track.id }

    # return next_track
    next_track = next_schedule_track.track
    Rails.logger.debug "next_track = #{next_track.title}"
    next_schedule_track
  rescue Exception => e
    Rails.logger.error ("[ScheduleService.get_next_schedule_track] there was an error = #{e}")
    nil
  end
end

这确实有效且易于阅读,但它也会产生多个SQL请求,所以我想知道是否有更好的方法?

以下是显示所有请求的日志。

 starting get next schedule
   ScheduleTrack Load (0.6ms)  SELECT "schedule_tracks".* FROM "schedule_tracks" WHERE "schedule_tracks"."schedule_id" = $1 AND "schedule_tracks"."position" = $2 ORDER BY "schedule_tracks"."position" ASC  [["schedule_id", 22], ["position", 2077]]
   ScheduleTrack Load (0.8ms)  SELECT "schedule_tracks".* FROM "schedule_tracks" WHERE "schedule_tracks"."schedule_id" = $1 AND "schedule_tracks"."position" = $2 ORDER BY "schedule_tracks"."position" ASC  [["schedule_id", 22], ["position", 2078]]
   Track Load (0.3ms)  SELECT  "tracks".* FROM "tracks" WHERE "tracks"."id" = $1 LIMIT $2  [["id", 74020682], ["LIMIT", 1]]
   Track Load (0.4ms)  SELECT  "tracks".* FROM "tracks" WHERE ("tracks"."id" != $1) ORDER BY RANDOM() LIMIT $2  [["id", 74020682], ["LIMIT", 1]]
    (0.1ms)  BEGIN
   ScheduleTrack Load (2.6ms)  SELECT  "schedule_tracks".* FROM "schedule_tracks" WHERE "schedule_tracks"."schedule_id" = $1 AND ("schedule_tracks"."position" IS NOT NULL) ORDER BY "schedule_tracks"."position" DESC LIMIT $2  [["schedule_id", 22], ["LIMIT", 1]]
   SQL (0.5ms)  INSERT INTO "schedule_tracks" ("position", "track_id", "schedule_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["position", 2078], ["track_id", 416171102], ["schedule_id", 22], ["created_at", "2017-10-30 12:04:22.470182"], ["updated_at", "2017-10-30 12:04:22.470182"]]
 [ActionCable] Broadcasting to schedule_channel: {:title=>"created track", :body=>4984}
    (0.6ms)  COMMIT
   ScheduleTrack Load (0.8ms)  SELECT "schedule_tracks".* FROM "schedule_tracks" WHERE "schedule_tracks"."schedule_id" = $1 AND "schedule_tracks"."position" = $2 ORDER BY "schedule_tracks"."position" ASC  [["schedule_id", 22], ["position", 2078]]
    (0.2ms)  BEGIN
   SQL (0.3ms)  UPDATE "schedules" SET "current_position" = $1, "updated_at" = $2 WHERE "schedules"."id" = $3  [["current_position", 2078], ["updated_at", "2017-10-30 12:04:22.482505"], ["id", 22]]
    (0.4ms)  COMMIT
    (0.2ms)  BEGIN
   SQL (0.5ms)  UPDATE "schedule_tracks" SET "state" = $1, "updated_at" = $2 WHERE "schedule_tracks"."id" = $3  [["state", "playing"], ["updated_at", "2017-10-30 12:04:22.487240"], ["id", 4983]]
    (0.5ms)  SELECT COUNT(*) FROM "schedule_tracks" WHERE "schedule_tracks"."schedule_id" = $1 AND ("schedule_tracks"."position" = 2077)  [["schedule_id", 22]]
    (0.6ms)  COMMIT
    (0.5ms)  BEGIN
   SQL (1.2ms)  UPDATE "schedule_tracks" SET "state" = $1, "updated_at" = $2 WHERE "schedule_tracks"."id" = $3  [["state", "next"], ["updated_at", "2017-10-30 12:04:22.504735"], ["id", 4984]]
    (2.0ms)  SELECT COUNT(*) FROM "schedule_tracks" WHERE "schedule_tracks"."schedule_id" = $1 AND ("schedule_tracks"."position" = 2078)  [["schedule_id", 22]]
    (0.9ms)  COMMIT
   ScheduleTrack Load (0.5ms)  SELECT "schedule_tracks".* FROM "schedule_tracks" WHERE "schedule_tracks"."schedule_id" = $1 AND "schedule_tracks"."position" = $2 ORDER BY "schedule_tracks"."position" ASC  [["schedule_id", 22], ["position", 2076]]
    (0.2ms)  BEGIN
   SQL (0.5ms)  UPDATE "schedule_tracks" SET "state" = $1, "updated_at" = $2 WHERE "schedule_tracks"."id" = $3  [["state", "played"], ["updated_at", "2017-10-30 12:04:22.519705"], ["id", 4982]]
    (0.6ms)  SELECT COUNT(*) FROM "schedule_tracks" WHERE "schedule_tracks"."schedule_id" = $1 AND ("schedule_tracks"."position" = 2076)  [["schedule_id", 22]]
    (0.6ms)  COMMIT
 [ActionCable] Broadcasting to schedule_channel: {:title=>"next position", :body=>4984}
   Track Load (0.4ms)  SELECT  "tracks".* FROM "tracks" WHERE "tracks"."id" = $1 LIMIT $2  [["id", 416171102], ["LIMIT", 1]]
 next_track = New York

有关最佳方法的建议将不胜感激 - 是否有更好的方法,或者这是一个很好的解决方案。

非常感谢!

1 个答案:

答案 0 :(得分:1)

在rails中编写干代码的最佳方法是执行以下操作:

<强> 1。使用MVC架构

这需要在模型,视图和控制器之间分离代码。基本概念是在Model Class中创建自己的自定义方法,这样您就可以多次调用该方法并在代码中保存很多行。

例如,

# check if next position is valid, otherwise pick random track
next_position = current_position + 1
next_schedule_track = scheduled_track_at_position(schedule, next_position)

将此代码移至Position.rb模型

def check_validity
    next_position = self + 1
    // perform the rest of your validity checks in hear
    // I don't know how to get hear the scheduled_track_at_position
end

而对于其余部分,在此我发现与Track模型,SceduleTrack模型和其他模型相关的内容......我相信你会通过scheduled_track_at_position来听取因为它是同一模型中的一种方法,它看起来像我一样混乱。

if next_schedule_track.nil?
  random_track  = Track.where.not(id: current_schedule_track.track.id).order("RANDOM()").first
  ScheduleTrack.create(track: random_track, schedule: schedule)
  next_schedule_track = scheduled_track_at_position(schedule, next_position)
end

Track.rb中创建random_track类方法

def self.random_track(schedule)
    self.where.not(id: schedule.track.id).order("RANDOM()").first
end

您可以使用

Song.rb调用该方法
Track.random_track(current_schedule_track.track.id)

但我可以通过在Song.rb

中创建方法来干这个
def track_id
    current_schedule_track.track.id
end

所以也许这可能会成功

Track.random_track(track_id)

<强> 2。使用继承和混合

干掉更多代码的第二种方法,不仅是将代码从Song.rb模块移动到Track.rb或其他Models中的相应方法,如果某个方法应该在Models上使用多个方法,但Model已经有parent,则创建Moduleextendinclude Module Class方法res.forEach((v, i) => { //Add the point here because this is where your processing happens I guess? this.points++; }); //Move it outside of your loop $timer = Observable.timer(100).subscribe((i) => {// $timer as a local variable. 1000/60 might be better as it will refresh for every frame. this.percentageCom = (this.points / this.PointsAdded) * 100; if (this.percentageCom === 100) { // this.isHidden = true; this.$timer.unsubscribe(); } });

在此post

中详细了解此信息

如果您还有其他问题,请问我。