如何找到同时拥有猫狗的用户?

时间:2011-06-27 06:13:40

标签: sql ruby-on-rails ruby-on-rails-3 activerecord

我想在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 - Bob)。

到目前为止,我的尝试是

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

现在我明白了为什么这不起作用 - 它正在寻找一只既是狗又是猫的宠物,所以什么都不返回。我不知道怎么修改这个给我答案我想要的。有没有人有什么建议?这看起来应该很容易 - 看起来并不是特别不寻常的情况。

--- ---编辑

我刚刚发现Squeel,只是在这个问题上添加一点点尾声。这允许您构建这样的子查询;

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

这将进入我的应用程序。

9 个答案:

答案 0 :(得分:10)

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'),这与现有版本的工作方式不同。

答案 1 :(得分:6)

使用子选择来约束结果:

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')

答案 2 :(得分:3)

通常的方法是在where子句中过滤猫或狗。然后在group byuser_id,并要求生成的论坛having count(distinct pet.id) = 2

我不确定你如何在ActiveRecord中表达having; this post似乎包含一种解决方法。

答案 3 :(得分:2)

对于这个问题,有很多方法可以获得解决方案 你也可以这样做......

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

答案 4 :(得分:1)

这应该会有所帮助。 试试这个

  

从用户u,pet p1中选择你。*   其中u.id = p1.user_id AND p1.animal =“cat”AND   p1.user_id in(选择来自宠物的user_id,其中animal ='dog')

答案 5 :(得分:1)

我很少回答关于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
                  ));

答案 6 :(得分:1)

有很多方法可以做到这一点 - 上面的一些方法可行;另外,这里有一个略微不同的方法,主要使用“视图” - 基本上,只是从你的通用'宠物'类继承到两个单独的类(猫和狗)。

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

答案 7 :(得分:1)

这是另一种解决方案。更多Rails友好..

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

关于使用JOINGROUP BY + HAVING进行此类解决方案的相对优点,请阅读此article

请参阅详细讨论此问题的SO question

答案 8 :(得分:0)

你可以通过各种方式得到这个结果。

希望它对你有所帮助。试试这个,

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