5

アクティブレコードを使用して次のクエリを実行したいと思います

(select *
from people p join cities c join services s
where p.city_id = c.id and p.id = s.person_id and s.type = 1)

intersect

(select *
from people p join cities c join services s
where p.city_id = c.id and p.id = s.person_id and s.type = 2)

問題は、まず、mysql が交差をサポートしていないことです。ただし、これは回避できます。問題は、アクティブレコードを取得して、それに近いものを出力できることです。

アクティブなレコードでは、複数のクエリを発行してreduce :& からそれらを結合するのが最善でしたが、リレーションではなく配列を取得しました。limit などを呼びたいのでそこが問題です。また交差は ruby​​ コードよりもデータベースで行った方が良いと思います。

4

2 に答える 2

8

あなたの質問は、おそらく次のような交差なしで解決できます。

Person.joins(:services).where(services: {service_type: [1,2]}).group(
   people: :id).having('COUNT("people"."id")=2')

ただし、以下は、ActiveRecord で交差のようなクエリを構築するために使用する一般的なアプローチです。

class Service < ActiveRecord::Base
  belongs_to :person

  def self.with_types(*types)
    where(service_type: types)
  end
end

class City < ActiveRecord::Base
  has_and_belongs_to_many :services
  has_many :people, inverse_of: :city
end

class Person < ActiveRecord::Base
  belongs_to :city, inverse_of: :people

  def self.with_cities(cities)
    where(city_id: cities)
  end

  def self.with_all_service_types(*types)
    types.map { |t|
      joins(:services).merge(Service.with_types t).select(:id)
    }.reduce(scoped) { |scope, subquery|
      scope.where(id: subquery)
    }
  end
end

Person.with_all_service_types(1, 2)
Person.with_all_service_types(1, 2).with_cities(City.where(name: 'Gold Coast'))

次の形式の SQL が生成されます。

SELECT "people".*
  FROM "people"
 WHERE "people"."id" in (SELECT "people"."id" FROM ...)
   AND "people"."id" in (SELECT ...)
   AND ...

各サブクエリが結果セットで一致する人物の ID を返す限り、条件/結合などに基づいて上記のアプローチで必要な数のサブクエリを作成できます。

各サブクエリの結果セットは AND で結合されるため、一致するセットがすべてのサブクエリの共通部分に制限されます。

アップデート

削除されたAR4を使用している場合scoped、私の他の回答は意味的に同等のscopedポリフィルを提供しますallが、ARのドキュメントが示唆しているものにもかかわらず、同等の代替品ではありません。ここで答えてください: Rails 4 では、Model.scoped は廃止されましたが、Model.all はそれを置き換えることはできません

于 2013-07-12T04:38:27.047 に答える
1

私は同じ問題に苦しんでいましたが、1 つの解決策しか見つかりませんでした。同じ関連付けに対する複数の参加です。結合用の SQL 文字列を作成しているので、これはあまりレール風ではないかもしれませんが、別の方法は見つかりませんでした。これは、任意の数のサービス タイプに対して機能します (都市は考慮に入れられていないように見えるため、分かりやすくするために join は省略されています)。

s = [1,2]
j = ''
s.each_index {|i|
  j += " INNER JOIN services s#{i} ON s.person_id = people.id AND s#{i}.type_id = #{s[i]}" 
}
People.all.joins(j)
于 2013-01-23T22:00:39.067 に答える