使用activerecord发出请求,仅从组中获取用户,而不从其他用户获取

我正在尝试从少数组中获取用户(使用给定的ID)并从其他组中排除用户。

我尝试过类似的东西:

User.joins(:groups).where(groups: {id: ["8939","8950"]}).where.not(groups: {id: 8942}).map(&:id) User Load (0.9ms) SELECT "users".* FROM "users" INNER JOIN "groups_users" ON "groups_users"."user_id" = "users"."id" INNER JOIN "groups" ON "groups"."id" = "groups_users"."group_id" WHERE "groups"."id" IN (8939, 8950) AND "groups"."id" != $1 [["id", 8942]] => [119491, 119489, 119490, 119492, 119488, 119484, 119483, 119491, 119482] 

但这不正确

8942组中的用户。

 Group.find(8942).users.pluck(:id) Group Load (0.4ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = $1 LIMIT 1 [["id", 8942]] (0.6ms) SELECT "users"."id" FROM "users" INNER JOIN "groups_users" ON "users"."id" = "groups_users"."user_id" WHERE "groups_users"."group_id" = $1 [["group_id", 8942]] => [119490, 119492, 119491, 119457, 119423] 

where.not不适用于用户"groups"."id" != $1 [["id", 8942]] 。 为什么?

正确的方法是使用SQL EXISTS条件。 我希望有一个特定的ActiveRecord辅助方法,但目前还没有。

好吧,使用纯SQL就好了:

 User.where("EXISTS (SELECT 1 FROM groups_users WHERE groups_users.user_id = users.id AND groups_users.group_id IN (?))", [8939, 8950]). where("NOT EXISTS (SELECT 1 FROM groups_users WHERE groups_users.user_id = users.id AND groups_users.group_id IN (?))", [8942]) 

您对原始查询所做的是要求[8942] ID加入您的查询组,并且只加入具有ID的组[8939, 8950] 。 好吧,你现在可以看到这没有任何意义:这就像要求选择每个名字都是bob而不是charlie 。 第二个条件不会向第一个条件添加任何内容。

连接查询是乘法列,因此如果您的用户位于每个组中,则结果集将为:

 user_id | group_id 1 | 8939 1 | 8950 1 | 8942 

然后你过滤掉后一行: 1 | 8942 1 | 8942 仍然,用户1在结果集中并返回。

并要求数据库返回不与另一个关系连接的记录,您应该明确使用明确存在的NOT EXISTS 🙂

现在有一个可以使用的Where Existsgem。 (完全披露:我最近创造了这个gem。)

有了它,您可以轻松完成以下任务:

 User.where_exists(:groups, id: [1, 2]).where_not_exists(:groups, id: [3, 4])