3

私は2つのモデルを持っています

class Foo < ActiveRecord::Base
  # columns are
  # max_spots

  has_many :bars
end

class Bar < ActiveRecord::Base
  # columns are
  # a_id

  belongs_to :foo
end

max_spots が関連するバーの数よりも大きいすべての Foo を取得する必要がありますが、次のように各 Foo を通過するのではなく、アクティブなレコードを介して行う必要があります。

class Foo
  #bad
  def self.bad_with_spots_left
    all.select do |foo|
      foo.max_spots - foo.bars.count > 0
    end 
  end

  #good but not working
  def self.good_with_spots_left
    joins(:bars).select('COUNT(bars.id) AS bars_count').where('max_spots - bars_count > 0')
  end
end

カウンター キャッシュを foo に追加するだけでよいことはわかっていますが、それなしでこれを行う方法を知りたいだけです。ありがとう!

4

2 に答える 2

1

foos現在受け入れられている回答の最初のソリューションは、テーブル内のすべての行に対して相互に関連するサブクエリが実行されるため、非効率的です。このようなシナリオでは、結合を使用することをお勧めします。

現在受け入れられている回答の2番目の解決策は、foosなしでは機能しませんbars

例:注文のない新製品。

この問題に対処するには、LEFT 外部結合を使用する必要があります。これとは別に、having句を使用してカウント比較を行うことができます。このようにして、操作全体がデータベースで処理されます。

def self.good_with_spots_left
  joins("LEFT OUTER JOIN bars bars ON bars.foo_id = foos.id").
  group('bars.foo_id').
  having("foos.max_spots > COUNT(COALESCE(bars.foo_id, 0))")
end

ノート:

このCOALESCEコマンドは、最初の null 以外の入力を返します。このコマンドは SQL92 と互換性があるため、データベース間で機能します。

を使用するCOALESCE理由

に一致するものがない場合のLEFT OUTER JOIN戻りNULL値。SQL操作はセット内の値を好まないため、値を に変換します。bars.foo_idbarsfooCOUNTNULLNULL0

于 2013-02-23T20:34:51.540 に答える
1

SQL では、WHERE 句でエイリアスを使用することはできませんが、列名のみを使用できます。

別の方法として、次のいずれかを試すことができます。

純粋な SQL で

def self.good_with_spots_left
  where('foos.max_spots > (SELECT Count(*) FROM bars WHERE bars.a_id = foos.id)')
end

または、ビットまたはルビを使用 (2 番目selectは &block を含むため、ルビで解釈されます)

def self.good_with_spots_left
  joins(:bars).select('foos.*', COUNT(bars.id) AS bars_count').group('bars.a_id').select{|foo| foo.max_spots > foo.bars_count}
end
于 2013-02-01T10:52:19.683 に答える