[英]Find all records which have a count of an association greater than zero
我正在嘗試做一些我認為很簡單但似乎並非如此的事情。
我有一個項目 model 有很多空缺。
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)
。
1) 要獲得至少有 1 個職位空缺的項目:
Project.joins(:vacancies).group('projects.id')
2) 要獲得超過 1 個空缺的項目:
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
規則可能需要手動指定?
joins
默認使用內部連接,因此使用Project.joins(:vacancies)
實際上只會返回具有關聯空缺的項目。
更新:
正如@mackskatz 在評論中指出的那樣,如果沒有group
子句,上面的代碼將為具有多個空缺的項目返回重復的項目。 要刪除重復項,請使用
Project.joins(:vacancies).group('projects.id')
更新:
正如@Tolsee 所指出的,您還可以使用distinct
。
Project.joins(:vacancies).distinct
舉個例子
[10] pry(main)> Comment.distinct.pluck :article_id
=> [43, 34, 45, 55, 17, 19, 1, 3, 4, 18, 44, 5, 13, 22, 16, 6, 53]
[11] pry(main)> _.size
=> 17
[12] pry(main)> Article.joins(:comments).size
=> 45
[13] pry(main)> Article.joins(:comments).distinct.size
=> 17
[14] pry(main)> Article.joins(:comments).distinct.to_sql
=> "SELECT DISTINCT \"articles\".* FROM \"articles\" INNER JOIN \"comments\" ON \"comments\".\"article_id\" = \"articles\".\"id\""
是的, vacancies
不是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')
對 has_many 表與group
或uniq
組合執行內部連接可能非常低效,在 SQL 中,這將更好地實現為使用EXISTS
和相關子查詢的半連接。
這允許查詢優化器探測空缺表以檢查是否存在具有正確 project_id 的行。 無論是一行還是一百萬具有該 project_id 都沒有關系。
這在 Rails 中沒有那么簡單,但可以通過以下方式實現:
Project.where(Vacancies.where("vacancies.project_id = projects.id").exists)
同樣,找到所有沒有空缺的項目:
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").exists)
編輯:在最近的 Rails 版本中,你會收到一個棄用警告,告訴你不要依賴exists
被委派給 arel。 解決這個問題:
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").arel.exists)
編輯:如果您對原始 SQL 感到不舒服,請嘗試:
Project.where.not(Vacancies.where(Vacancy.arel_table[:project_id].eq(Project.arel_table[:id])).arel.exists)
您可以通過添加類方法來隱藏arel_table
的使用, arel_table
減少混亂,例如:
class Project
def self.id_column
arel_table[:id]
end
end
……所以……
Project.where.not(
Vacancies.where(
Vacancy.project_id_column.eq(Project.id_column)
).arel.exists
)
在 Rails 4+ 中,您還可以使用includes或aging_load來獲得相同的答案:
Project.includes(:vacancies).references(:vacancies).
where.not(vacancies: {id: nil})
Project.eager_load(:vacancies).where.not(vacancies: {id: nil})
我認為有一個更簡單的解決方案:
Project.joins(:vacancies).distinct
沒有太多的 Rails 魔法,你可以這樣做:
Project.where('(SELECT COUNT(*) FROM vacancies WHERE vacancies.project_id = projects.id) > 0')
這種類型的條件適用於所有 Rails 版本,因為大部分工作是直接在 DB 端完成的。 另外,鏈接.count
方法也能很好地工作。 我之前被像Project.joins(:vacancies)
這樣的查詢所Project.joins(:vacancies)
。 當然,它有利有弊,因為它與數據庫無關。
您還可以將EXISTS
與SELECT 1
一起使用,而不是從vacancies
表中選擇所有列:
Project.where("EXISTS(SELECT 1 from vacancies where projects.id = vacancies.project_id)")
如果我想知道有多少條記錄至少有一條關聯記錄,我會這樣做:
Project.joins(:vacancies).uniq.count
錯誤是告訴您,職位空缺基本上不是項目中的一欄。
這應該工作
Project.joins(:vacancies).where('COUNT(vacancies.project_id) > 0')
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.