私はそれが簡単だと思ったが、そうではないように思える何かをしようとしています。
私は多くの欠員があるプロジェクトモデルを持っています。
class Project < ActiveRecord::Base
has_many :vacancies, :dependent => :destroy
end
少なくとも1つの空きがあるプロジェクトをすべて取得したいです。私はこのようなものを試しました:
Project.joins(:vacancies).where('count(vacancies) > 0')
しかし、それは言います
SQLite3::SQLException: no such column: vacancies: SELECT "projects".* FROM "projects" INNER JOIN "vacancies" ON "vacancies"."project_id" = "projects"."id" WHERE ("projects"."deleted_at" IS NULL) AND (count(vacancies) > 0)
。
joins
はデフォルトで内部結合を使用するため、Project.joins(:vacancies)
を使用すると、空席が関連付けられているプロジェクトのみが有効になります。
更新:
@mackskatzがコメントで指摘したように、group
句なしで、上記のコードは複数の空席があるプロジェクトに対して重複プロジェクトを返します。重複を削除するには、使用します
Project.joins(:vacancies).group('projects.id')
1)少なくとも1つの空きがあるプロジェクトを取得するには:
Project.joins(:vacancies).group('projects.id')
2)複数の空きがあるプロジェクトを取得するには:
Project.joins(:vacancies).group('projects.id').having('count(project_id) > 1')
3)または、Vacancy
モデルがカウンターキャッシュを設定する場合:
belongs_to :project, counter_cache: true
これも機能します:
Project.where('vacancies_count > ?', 1)
vacancy
の屈折規則は、 手動で指定 ?
ええ、vacancies
は結合のフィールドではありません。私はあなたが望むと信じています:
Project.joins(:vacancies).group("projects.id").having("count(vacancies.id)>0")
# None
Project.joins(:vacancies).group('projects.id').having('count(vacancies) = 0')
# Any
Project.joins(:vacancies).group('projects.id').having('count(vacancies) > 0')
# One
Project.joins(:vacancies).group('projects.id').having('count(vacancies) = 1')
# More than 1
Project.joins(:vacancies).group('projects.id').having('count(vacancies) > 1')
Rails 4+では、includesまたはeager_loadを使用して同じ答えを得ることができます。
Project.includes(:vacancies).references(:vacancies).
where.not(vacancies: {id: nil})
Project.eager_load(:vacancies).where.not(vacancies: {id: nil})
Has_manyテーブルへの内部結合をgroup
またはuniq
と組み合わせて実行することは潜在的に非常に非効率的であり、SQLでは、相関サブクエリでEXISTS
を使用する準結合としてより適切に実装されます。
これにより、クエリオプティマイザーは空席テーブルをプローブして、正しいproject_idを持つ行の存在を確認できます。そのproject_idを持つ行が1行であろうと100万行であろうと関係ありません。
Railsでは簡単ではありませんが、次の方法で実現できます。
Project.where(Vacancies.where("vacancies.project_id = projects.id").exists)
同様に、空席がないすべてのプロジェクトを検索します。
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").exists)
もっと簡単な解決策があると思います:
Project.joins(:vacancies).uniq
多くのRails魔法がなければ、次のことができます。
Project.where('(SELECT COUNT(*) FROM vacancies WHERE vacancies.project_id = projects.id) > 0')
このタイプの条件は、すべてのRailsバージョンで機能します。これは、多くの作業がDB側で直接行われるためです。さらに、チェーン.count
メソッドもうまく機能します。以前にProject.joins(:vacancies)
のようなクエリに焼かれました。もちろん、DBに依存しないため、長所と短所があります。