Rails:ActiveRecord查询,其中所有关联都是/不是值

时间:2018-10-19 03:23:48

标签: ruby-on-rails activerecord

Rails: ActiveRecord query based on association value为例,

如果我们要查找具有任何相关servers'company_id5的报告,则可以写为 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来实现吗?

2 个答案:

答案 0 :(得分:0)

我非常怀疑只能使用where来完成此操作。但是您的示例还包含includes调用,因此我假设您的意思是“是否可以使用ActiveRecord方法完成并使用尽可能少的原始SQL”。好吧,这是可以实现的。

如何仅使用具有给定company_id的服务器来获取报告

这是经典的SELECT查询,其INNER JOIN在reportsservers之间。由于我们需要查看报告的所有服务器,因此我们按其ID对报告进行分组,并添加HAVING以查找给定报告的max中的minservers.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的报告),然后排除它们。因此,我们可以获取具有带有其他company_id的服务器的报告以及不具有服务器的报告。

Report.where.not(
  id: Report.joins(:servers).where(servers: { company_id: 5 })
)

答案 1 :(得分:0)

如何查找所有相关服务器的company_id为5的报告?

这可以通过首先找到至少一个具有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

如果有些报告记录没有关联的服务器记录,那么joins将产生与left_joins不同的结果。 left_joins排除此类报告,而joins包括这些报告。

由您确定哪种行为是正确的。如果您知道永远不会有没有关联服务器的报告,那么这两种方法都可以工作(使用joins可能是最好的方法,但是您应该始终查看特定于数据库的性能含义)。

查找所有相关服务器的company_id不为5的报告吗?

这里的原理是相同的。首先,找到具有company_id为5的服务器的报告集,然后选择不在该报告集中的所有其他报告。

reports_to_exclude = Report.joins(:servers).where(servers: { company_id: 5 })
Report.where.not(id: reports_to_exclude)

我可以只在哪里使用吗?

这是我所能找到的。