使用内部联接Rails ActiveRecord查询
我有两个表,我正在尝试进行内部联接。
一个是主键为id
的users
表。
另一个表是其中user_id
是外键的bars
。 bars
还有一个名为foo_id
的列,其中food_id
是foos
表的外键。
我正在尝试将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)的条件非常重要,这样才能使连接完成而不是在连接后进行过滤。