web-dev-qa-db-ja.com

ゼロより大きい関連付けのカウントを持つすべてのレコードを検索します

私はそれが簡単だと思ったが、そうではないように思える何かをしようとしています。

私は多くの欠員があるプロジェクトモデルを持っています。

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)

86
JPHorta

joinsはデフォルトで内部結合を使用するため、Project.joins(:vacancies)を使用すると、空席が関連付けられているプロジェクトのみが有効になります。

更新:

@mackskatzがコメントで指摘したように、group句なしで、上記のコードは複数の空席があるプロジェクトに対して重複プロジェクトを返します。重複を削除するには、使用します

Project.joins(:vacancies).group('projects.id')
56
jvnill

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の屈折規則は、 手動で指定

146
Arta

ええ、vacanciesは結合のフィールドではありません。私はあなたが望むと信じています:

Project.joins(:vacancies).group("projects.id").having("count(vacancies.id)>0")
29
Peter Alfvin
# 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')
14
Dorian

Rails 4+では、includesまたはeager_loadを使用して同じ答えを得ることができます。

Project.includes(:vacancies).references(:vacancies).
        where.not(vacancies: {id: nil})

Project.eager_load(:vacancies).where.not(vacancies: {id: nil})
3
konyak

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)
3
David Aldridge

もっと簡単な解決策があると思います:

Project.joins(:vacancies).uniq
2
Yuri Karpovich

多くのRails魔法がなければ、次のことができます。

Project.where('(SELECT COUNT(*) FROM vacancies WHERE vacancies.project_id = projects.id) > 0')

このタイプの条件は、すべてのRailsバージョンで機能します。これは、多くの作業がDB側で直接行われるためです。さらに、チェーン.countメソッドもうまく機能します。以前にProject.joins(:vacancies)のようなクエリに焼かれました。もちろん、DBに依存しないため、長所と短所があります。

1
konyak