2

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

一个是users主键为 的表id

另一个表是外键bars在哪里。还有一个名为where的列是表的外键。user_idbarsfoo_idfood_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 行。

我在这里到底做错了什么?

4

4 回答 4

2

尝试使用这个

    User.includes(:bars).where("users.created_at <= ?", 50.days.ago).where("bars.foo_id != 5")
于 2012-11-08T05:19:22.597 回答
2

你的连接数学是错误的,它的效果是为每个用户 + foo 组合创建行。这就是完全连接的工作方式。出现此错误的原因是您实际上并未将 bar 表连接到 users 表。通常你必须加入一个条件,就像在你的情况下bars.user_id=users.id是个好主意。

话虽如此,您要做的是确定哪些用户符合条件,然后加载这些用户:

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

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

于 2012-11-08T04:38:10.430 回答
1

这应该工作 -

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'
于 2012-11-08T05:43:25.277 回答
0

因为它是您的加入,所以对于满足 where 子句的每个用户/栏条件,结果集中都会出现一行:如果用户有 5 个栏,它将出现 5 次。

你不能只用一个 distinct 来解决这个问题,因为这会选择所有可以实现连接的行:所有具有至少 1 个 bar 且 foo_id 不是 5 的用户,而不是所有没有此类 bar 的用户

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

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) 加入用户。因为它是左连接,如果不存在这样的条,则将在结果集中返回一行,其中条表的所有列都设置为空,然后您可以对其进行过滤。重要的是在 ON 子句中的条 (foo_id = 5) 上有条件,以便它有助​​于连接的完成方式,而不是在连接后进行过滤。

于 2012-11-08T09:09:07.533 回答