1


0

railsアプリのモデルがsong、play、radio_stationであると仮定します。song has_manyが再生しますhas_many radio_stationsが再生しますradio_stationは属性 "call_sign"を持ちます

私は、(曲の)最後のプレイが "WKRP"のラジオ局でプレイされたすべての曲を返すようにSong用の検索を作成したいと思います。

私はそれをできた

found = [] Song.find(:all、:include ⇒ [\ {:plays ⇒ [:radio_station]}])。それぞれdo | song | << song song.plays.last.radio_station.call_sign == "WKRP"の場合に見つかりましたend

しかし、これはすべての曲が最初にDBからプルダウンされ、ループされなければならないことを意味するでしょう…​ 曲や演劇の数が増えるにつれて遅くなります。

これをどのようにして1つの検索条件にできますか

これは実行可能であるべきもののように思えます - しかし、私はどのように理解することができないし、私は本当にSQLの教祖ではありません…​

その後.. 私はそれを次のように呼ぶことができるように名前付きスコープにそれを絞り込むのが大好きだ:

Song.find_all_last_played_at( "WKRP")

5 Answer


1


これはおそらくActiveRecordファインダーではできません。 あなたはいくつかのカスタムSQLをサブクエリでロールする必要があります(MySQL 5が必要です):

SELECT s.* FROM songs s LEFT JOIN plays p
 WHERE p.radio_station_id = [id of WKRP]
   AND  p.created_at = (SELECT MAX(created_at) FROM plays p2
                         WHERE p2.song_id = s.id)

`find_by_sql`を使ってこれらの曲をロードします。

Song.find_by_sql('SELECT ...')

望むなら `named_scope`を使ってリファクタリングできます。

class Song < ActiveRecord::Base
  has_many :plays

  named_scope :last_played_on, lambda { |radio_station|
    { :conditions => ['plays.created_at = (SELECT MAX(p2.created_at) FROM plays p2
        WHERE p2.song_id = songs.id) AND plays.radio_station_id = ?', radio_station.id],
      :joins => [:plays] }
  }
end

@wkrp = RadioStation.find_by_name('WKRP')
Song.last_played_on(@wkrp).each { |song| ... }

サブクエリを使用すると遅くなることがあります。 クエリをより簡単かつ迅速にするために、ある曲の最新の play_id`を songs`テーブルのフィールドとしてキャッシュすることをお勧めします。 まず、曲のテーブルに `last_play_id`フィールドを追加してください。

class Play < ActiveRecord::Base
  belongs_to :song

  after_create do |play|
    play.song.last_play_id = play.id
    play.song.save!
  end
end


0


おそらく名前付きスコープを見てください。

クラスSong named_scope:last_played_by、lambda {| call_sign | {:conditions => ["plays.radio_stations.call_sign =?"、call_sign]}} end

あなたはそのようにそれを使うでしょう

Song.last_played_by( "WKRZ")

しかし、私はあなたが最後のプレーを見つけるためにどのようなメカニズムを使っているのかわからないことに気づきました。 しかし、それを次のように2つの名前付きスコープに分割することができます。

クラスSong named_scope:plays_by、lambda {| call_sign | {:conditions => ["plays.radio_station.call_sign =?"、call_sign]}} named_scope:last_play {:conditions => ["MAX(plays.created_at)"] end

それを使用するには:

Song.last_play.played_by( "WKRZ")

私はそれが正しいと思います、しかし私はそれをテストしていません、そして私は名前付きスコープに精通しているすべてではありません。


0


パフォーマンスのためには、おそらくwvangergenが提案することをするべきです。 しかし、合成糖については、私はこれがうまくいくと思います:

クラスSong has_many:plays、:order => 'created_at' do def last_played self.maximum(:created-at)終了終了終了

class Play named_scope:by、lambda {| call_sign |}を再生します。 {:conditions => ["radio_stations.call_sign =?"、call_sign]}} end

使用するには:

Song.plays.last_played.by( "WKRZ")

コードをテストしているのではないので、構文エラーをお詫びします。


0


私はあなたがあなたのアソシエーションから始めることができると思います(デフォルトの 'order’は 'created_at’です)

クラス曲has_many:plays has_many:radio_staions、:through =>:plays、:uniq => true has_one:last_radio_station、:through =>:plays、:source =>:last_radio_station

named_scope:last_played_at、lambda {| call_sign | {:include =>:last_radio_station、:conditions => ["radio_stations.call_sign =?"、call_sign]}} end

これはあなたのPlayモデルが持つことを期待しています:

クラスPlay has_one:last_radio_station、:class_name => 'RadioStation'、:order => 'created_at' has_many:radio_stations

それからあなたは単に呼ぶことができます:

Song.last_played_at( "WKRZ")

私は自分で試したことはありませんが、うまくいけばうまくいくでしょう。


0


'created_at DESC’の順序でその特定のラジオ局のためのすべての演劇を引っ張り、次に返された問い合わせの最後まで.lastを連鎖するnamed_scopeを作成しました。 に沿って:

Song.played_by( 'WRXD')。