Как мне найти пользователя, у которого есть и кошка, и собака? - PullRequest
10 голосов
/ 27 июня 2011

Я хочу выполнить поиск по 2 таблицам, которые имеют отношение многие-к-одному, например,

class User << ActiveRecord::Base
  has_many :pets
end

class Pet << ActiveRecord::Base
  belongs_to :users
end

Теперь, скажем, у меня есть такие данные, как

users

id        name
1         Bob
2         Joe
3         Brian

pets

id        user_id  animal
1         1        cat
2         1        dog
3         2        cat
4         3        dog

Я хочу создать запрос активной записи, который будет возвращать пользователя, у которого есть и кошка, и собака (т. Е. Пользователь 1 - Боб).

Моя попытка сделать это пока

User.joins(:pets).where('pets.animal = ? AND pets.animal = ?','dog','cat')

Теперь я понимаю, почему это не работает - он ищет питомца, который является собакой и кошкой, поэтому ничего не возвращает.Я не знаю, как изменить это, чтобы дать мне ответ, который я хочу, однако.У кого-нибудь есть предложения?Кажется, что это должно быть легко - это не кажется особенно необычной ситуацией.

--- edit ---

Просто добавив немного кода в этот вопрос, как я только чтообнаружен Squeel .Это позволяет вам создавать подзапрос следующим образом:

User.where{id.in(Pet.where{animal == 'Cat'}.select{user_id} & id.in(Pet.where{animal == 'Dog'}.select{user_id}))

Это то, что попадет в мое приложение.

Ответы [ 9 ]

10 голосов
/ 28 июня 2011

Andomar - К сожалению, написание такого запроса не всегда будет работать должным образом. В частности, наличие 2 кошек приведет к появлению пользователя, а наличие 3 домашних животных, скажем, 2 кошек и собаки, приведет к их исключению.
Я не знаю много об ActiveRecord, но вот стандартный синтаксис SQL, который будет работать:

SELECT users.id
FROM Users
JOIN (SELECT user_id 
      FROM Pets
      WHERE animal IN ('dog', 'cat')
      GROUP BY user_id
      HAVING COUNT(DISTINCT animal)) Pets
  ON Pets.user_id = Users.id

Это работает иначе, чем в существующих версиях, считая отдельный «тип» питомца ('cat' против 'dog').

6 голосов
/ 30 июня 2011

Используйте подвыборы, чтобы ограничить результаты:

User.joins(:pets).where(
  'id IN (SELECT user_id FROM pets WHERE animal = ?) AND
   id IN (SELECT user_id FROM pets WHERE animal = ?)',
  'cat', 'dog')
3 голосов
/ 27 июня 2011

Обычный подход - фильтровать кошек ИЛИ собак в предложении where.Затем вы group by на user_id и требуете, чтобы получившаяся группа having count(distinct pet.id) = 2.

Я не уверен, как вы выражаете having в ActiveRecord; этот пост , кажется, содержит обходной путь.

2 голосов
/ 04 июля 2011

для этого вопроса есть так много способов получить решение, которое вы можете сделать, также следуйте ...

select dog_table.user_name from 
(
    select * 
    FROM users,pets  
    where pets.user_id = users.id 
    and  pets.animal = 'dog'
) dog_table,
(
    select * 
    FROM users,pets  
    where pets.user_id = users.id 
    and  pets.animal = 'cat'
) cat_table
where dog_table.user_id = cat_table.user_id
1 голос
/ 15 августа 2011

Вот еще одно решение.Немного больше Rails friendly.

User.all(:select => "DISTINCT users.*",
 :joins=>[:pets, :pets],
 :conditions => ["pets.animal = ? AND pets_users.animal = ?", "cat", "dog"])

Прочтите эту статью о преимуществах использования JOIN против GROUP BY + HAVING для такого решения.

Обратитесь к этому SO вопросу , в котором подробно обсуждается эта проблема.

1 голос
/ 06 июля 2011

Есть несколько способов сделать это - некоторые из вышеперечисленных будут работать; также, здесь есть немного другой подход, который использует по существу «представления» - по сути, просто наследуя от вашего общего класса «домашние животные» в два отдельных класса (кошки и собаки).

SELECT
   id,
   name
FROM
      users
   INNER JOIN
      (
      SELECT DISTINCT
         user_id as belongs_to
      FROM
         pets
      WHERE
         animal = 'dog'
      ) dog
   ON users.id = dog.belongs_to
   INNER JOIN
      (
      SELECT DISTINCT
         user_id as belongs_to
      FROM
         pets
      WHERE
         animal = 'cat'
      ) cat
   ON users.id = cat.belongs_to
1 голос
/ 04 июля 2011

Я редко отвечаю на вопросы о SO, но я попробую.:)

SELECT name
FROM users
WHERE id IN (SELECT a.user_id
             FROM (
                    (SELECT user_id FROM pets WHERE animal = 'cat') a
                    INNER JOIN
                    (SELECT user_id FROM pets WHERE animal = 'dog') b
                    ON a.user_id = b.user_id
                  ));
1 голос
/ 04 июля 2011

Это должно быть полезно.попробуйте это

выберите u. * из пользователей u, домашних животных p1, где u.id = p1.user_id AND p1.animal = "cat" и p1.user_id in (выберите user_id из домашних животных, где animal ='собака')

0 голосов
/ 05 июля 2011

Вы можете получить этот результат различными способами.

Надеюсь, это поможет вам. попробуйте это,

SELECT id FROM (SELECT user_id AS id FROM users
INNER JOIN pets ON pets.user_id=users.id
GROUP BY pets.user_id,pets.animal
HAVING COUNT(pets.user_id)>0 AND (pets.animal='cat' OR pets.animal='dog')
)AS s GROUP BY id HAVING COUNT(id)>1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...