以Rails: ActiveRecord query based on association value为例,
如果我们要查找具有任何相关servers
'company_id
为5
的报告,则可以写为
Report.includes(:servers).where(servers: {company_id: 5})
鉴于报表中有多个相关服务器,其中一些服务器的company_id为5,一些server_id为6。
假设
report_a
拥有2台服务器,分别具有company_id,5,6 report_b
有2台服务器,每台服务器的company_id为5 report_c
有2台服务器,每台服务器的company_id为6 如何查找所有相关服务器的company_id为5的报告(只有所有相关服务器的company_id为5,则报告合格)
在上述情况下,report_b
是答案
查找所有相关服务器的company_id不为5的报告吗(只有所有相关服务器的company_id不是5时,该报告才合格)
在上述情况下,report_c
是答案
我只能使用where
来实现吗?
答案 0 :(得分:0)
我非常怀疑只能使用where
来完成此操作。但是您的示例还包含includes
调用,因此我假设您的意思是“是否可以使用ActiveRecord方法完成并使用尽可能少的原始SQL”。好吧,这是可以实现的。
这是经典的SELECT查询,其INNER JOIN在reports
和servers
之间。由于我们需要查看报告的所有服务器,因此我们按其ID对报告进行分组,并添加HAVING以查找给定报告的max
中的min
和servers.company_id
。我们正在寻找的报告有max(company_id) == min(company_id) == 5
:
company_id = 5
Report.joins(:servers)
.group('reports.id')
.having("max(servers.company_id) = #{company_id} AND min(servers.company_id) = #{company_id}")
最好的方法是获取那些不符合给定条件的报告(即,具有服务器的给定company_id
的报告),然后排除它们。因此,我们可以获取具有带有其他company_id的服务器的报告以及不具有服务器的报告。
Report.where.not(
id: Report.joins(:servers).where(servers: { company_id: 5 })
)
答案 1 :(得分:0)
这可以通过首先找到至少一个具有company_id = 5的服务器的报告集来完成,然后只需查找不在该集中的报告即可。
server_conditions = Server.where.not(company_id: 5)
.or(Server.where(id: nil)) # Include reports which have NO servers.
reports_to_exclude = Report
.left_joins(:servers) # see notes about joins vs left_joins
.merge(server_conditions)
Report.where.not(id: reports_to_exclude) # ActiveRecord automatically selects `id` from reports_to_exclude, but it could be done explicitly, id: reports_to_exclude.select(:id)
如果有些报告记录没有关联的服务器记录,那么joins
将产生与left_joins
不同的结果。 left_joins
将排除此类报告,而joins
将包括这些报告。
由您确定哪种行为是正确的。如果您知道永远不会有没有关联服务器的报告,那么这两种方法都可以工作(使用joins
可能是最好的方法,但是您应该始终查看特定于数据库的性能含义)。
这里的原理是相同的。首先,找到具有company_id为5的服务器的报告集,然后选择不在该报告集中的所有其他报告。
reports_to_exclude = Report.joins(:servers).where(servers: { company_id: 5 })
Report.where.not(id: reports_to_exclude)
这是我所能找到的。