JPH*_*rta 91 sql activerecord ruby-on-rails ruby-on-rails-3
我正在尝试做一些我认为很简单的事情,但似乎并不是这样.
我有一个有很多职位空缺的项目模型.
class Project < ActiveRecord::Base
has_many :vacancies, :dependent => :destroy
end
Run Code Online (Sandbox Code Playgroud)
我想得到所有至少有1个空缺的项目.我试过这样的事情:
Project.joins(:vacancies).where('count(vacancies) > 0')
Run Code Online (Sandbox Code Playgroud)
但它说
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)
.
Art*_*rta 154
1)获得至少有1个空缺的项目:
Project.joins(:vacancies).group('projects.id')
Run Code Online (Sandbox Code Playgroud)
2)获得超过1个空缺的项目:
Project.joins(:vacancies).group('projects.id').having('count(project_id) > 1')
Run Code Online (Sandbox Code Playgroud)
3)或者,如果Vacancy
模型设置计数器缓存:
belongs_to :project, counter_cache: true
Run Code Online (Sandbox Code Playgroud)
那么这也会奏效:
Project.where('vacancies_count > ?', 1)
Run Code Online (Sandbox Code Playgroud)
vacancy
可能需要手动指定变形规则?
jvn*_*ill 57
joins
默认情况下使用内连接,因此使用Project.joins(:vacancies)
will实际上只返回具有关联空位的项目.
更新:
正如@mackskatz在评论中指出的那样,如果没有group
条款,上面的代码将为具有多个空缺的项目返回重复项目.要删除重复项,请使用
Project.joins(:vacancies).group('projects.id')
Run Code Online (Sandbox Code Playgroud)
Pet*_*vin 31
是的,vacancies
不是加入中的一个字段.我相信你想:
Project.joins(:vacancies).group("projects.id").having("count(vacancies.id)>0")
Run Code Online (Sandbox Code Playgroud)
Dor*_*ian 15
# 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')
Run Code Online (Sandbox Code Playgroud)
对has_many 表执行内部连接并结合a group
oruniq
可能非常低效,并且在SQL 中这将更好地实现为EXISTS
与相关子查询一起使用的半连接。
这允许查询优化器探测空缺表以检查是否存在具有正确 project_id 的行。无论是一行还是一百万具有该 project_id 都没有关系。
这在 Rails 中没有那么简单,但可以通过以下方式实现:
Project.where(Vacancies.where("vacancies.project_id = projects.id").exists)
Run Code Online (Sandbox Code Playgroud)
同样,找到所有没有空缺的项目:
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").exists)
Run Code Online (Sandbox Code Playgroud)
编辑:在最近的 Rails 版本中,您会收到一个弃用警告,告诉您不要依赖exists
被委派给 arel。解决这个问题:
Project.where.not(Vacancies.where("vacancies.project_id = projects.id").arel.exists)
Run Code Online (Sandbox Code Playgroud)
编辑:如果您对原始 SQL 感到不舒服,请尝试:
Project.where.not(Vacancies.where(Vacancy.arel_table[:project_id].eq(Project.arel_table[:id])).arel.exists)
Run Code Online (Sandbox Code Playgroud)
您可以通过添加类方法来隐藏 的使用,从而减少混乱arel_table
,例如:
class Project
def self.id_column
arel_table[:id]
end
end
Run Code Online (Sandbox Code Playgroud)
... 所以 ...
Project.where.not(
Vacancies.where(
Vacancy.project_id_column.eq(Project.id_column)
).arel.exists
)
Run Code Online (Sandbox Code Playgroud)
在Rails 4+中,您还可以使用includes或eager_load来获得相同的答案:
Project.includes(:vacancies).references(:vacancies).
where.not(vacancies: {id: nil})
Project.eager_load(:vacancies).where.not(vacancies: {id: nil})
Run Code Online (Sandbox Code Playgroud)