使用内部联接Rails ActiveRecord查询

我有两个表,我正在尝试进行内部联接。

一个是主键为idusers表。

另一个表是其中user_id是外键的barsbars还有一个名为foo_id的列,其中food_idfoos表的外键。

我正在尝试将ActiveRecord查询放在一起,我可以选择在N天前或之前创建的所有用户,并且没有任何foos ,其中bars.foo_id等于特定的id。 我尝试过这样的事情:

 users = User.where("users.created_at <= ?", 50.days.ago).joins(:bars).where("bars.foo_id != 5") 

此查询字段超过30,000个结果,这是不正确的,因为Users表只有12,000行。

我到底错在了什么?

您的连接数学错误,它会为每个用户+ foo组合创建行。 这是完整连接的工作方式。 这种滑动的原因是因为您实际上没有将bars表连接到users表。 通常你必须加入条件,就像在你的情况下bars.user_id=users.id是个好主意。

话虽这么说,你想要做的是确定哪些用户符合条件,然后加载:

 users = User.where('id IN (SELECT DISTINCT user_id FROM bars WHERE bars.foo_id!=?)', 5) 

这个子选择,如果单独运行,应该只返回一个没有特定foo的用户列表。 将此作为WHERE条件使用时,应仅加载这些用户。

尝试使用这个

  User.includes(:bars).where("users.created_at <= ?", 50.days.ago).where("bars.foo_id != 5") 

这应该工作 –

 User.joins(:bars).where("bars.foo_id != ? and users.created_at <= ?", 5, 50.days.ago).select("distinct users.*") 

它将生成以下sql -

  select distinct users.* from users INNER JOIN bars on bars.user_id = user.id WHERE bars.foo_id != 5 and users.created_at <= '2012-09-19 10:59:54' 

因为它是你的连接将在结果集中为满足where子句的每个用户/条条件导致一行:如果用户有5个条,它将出现5次。

你不能只用一个独特的方法解决这个问题,因为它会选择可以实现连接的所有行:所有具有至少1个条形的用户,其foo_id不是5而不是所有没有这种条形的用户

您可以使用左连接执行此操作:

 User.joins("left join bars on bars.user_id = users.id and bars.foo_id = 5").where("users.created_at < ? AND bars.id is null", 50.days.ago") 

这会尝试将“坏”栏(foo_id = 5)加入用户。 因为它是左连接,如果不存在这样的条,那么将在结果集中返回一行,其中bars表的所有列都设置为null,然后您可以对其进行过滤。 在ON子句中使用条形(foo_id = 5)的条件非常重要,这样才能使连接完成而不是在连接后进行过滤。