2015-08-13 1 views
0

Я пытаюсь получить пользователей из нескольких групп (с заданными идентификаторами) и исключить пользователей из других групп.Сделайте запрос с activerecord, чтобы получать только пользователей от групп, а не от других.

Я пытался что-то вроде:

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] 

Но это не правильно

Пользователям в группе .

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] 

The where.not не работает на пользователя "groups"."id" != $1 [["id", 8942]]. Зачем ?

ответ

1

Правильный способ выполнения таких действий - использовать условие 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] идентификаторами на ваш запрос, и только присоединяющихся группы с ids [8939, 8950]. Ну, теперь вы можете видеть, что это не имеет никакого смысла: это как просить выбрать каждого пользователя с именем bob и NOT charlie. Второе условие ничего не добавляет к первому.

Регистрация запроса перемножения столбцов, так что если пользователь находится в каждой группе, результирующий набор будет:

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

Затем отфильтровать последнюю строку: 1 | 8942. Тем не менее, пользователь 1 находится в результирующем наборе и возвращается.

И спросить базу данных, чтобы вернуть только записи, которые не соединяются с другим соотношением вы должны явно использовать NOT EXISTS, которая существует специально для этой цели :)

+0

Благодаря EugZol снова. Ты мой парень поддержки «НЕ СУЩЕСТВУЕТ». Мне определенно нужно узнать больше sql-запроса. С той, которую вы мне даете, у меня небольшая ошибка. 'PG :: UndefinedTable: ERROR: отсутствует запись FROM-clause для таблицы" groups_users " LINE 1: ..." users "WHERE (EXISTS (SELECT 1 FROM groups WHERE groups_use ...' – Mio

+1

Ха-ха, я действительно ' EXISTING' (экзистенциальный?) Support guy :) 'WHERE groups_use' - это ваша опечатка, измените' _' на '.' (' groups.user_id'). – EugZol

+0

'PG :: UndefinedColumn: ERROR: column groups.user_id делает не существует LINE 1: ... «users» WHERE (EXISTS (SELECT 1 FROM groups WHERE groups.use ... 'с вашей версией – Mio

1

Существует теперь Where Exists драгоценный камень, который вы можете использовать , (Полное раскрытие: Я создал этот драгоценный камень в последнее время.)

С его помощью вы можете достичь своей задачи просто:

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

Фантастический. Любить это – Mio

Смежные вопросы