簡體   English   中英

查找關聯計數大於零的所有記錄

[英]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 表與groupuniq組合執行內部連接可能非常低效,在 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+ 中,您還可以使用includesaging_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) 當然,它有利有弊,因為它與數據庫無關。

您還可以將EXISTSSELECT 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM