我可以使用符合特定条件的多个关联记录查询记录吗?

时间:2015-11-06 20:22:45

标签: mysql sql ruby-on-rails ruby

我有两个表,ShowCharacter。每个Show has_many Characters。

class Show < ActiveRecord::Base
  has_many :characters

class Character < ActiveRecord::Base
  belongs_to :show

我想要做的是返回与符合特定条件的多个字符相关联的节目的结果。

例如,我希望能够返回一个包含Batman和Robin字符的节目列表。不是蝙蝠侠或罗宾,蝙蝠侠和罗宾。

所以查询应该是

Show.includes(:characters).where(characters: {'name = ? AND name = ?', "Batman", "Robin"})

但这会返回错误。有没有可行的语法?

更新

查询

Show.includes(:characters).where('characters.name = ? AND characters.name = ?', "Batman", "Robin")

返回值0,即使有明确的节目与蝙蝠侠和罗宾相关联。

7 个答案:

答案 0 :(得分:11)

使用纯SQL,一个解决方案是:

select s. * 
from shows as s 
join characters as c1 on (s.id=c1.show_id) 
join characters as c2 on (s.id=c2.show_id) 
where c1.name='Batman' 
and   c2.name='Robin';

使用Arel,我会翻译为:

Show.joins('join characters as c1 on shows.id=c1.show_id').joins('join
characters as c2 on shows.id=c2.show_id').where('c1.name = "Batman"').where(
'c2.name="Robin"')

答案 1 :(得分:6)

所以你必须在这里看一点SQL;特别是如果你希望它具有高性能并处理不同类型的匹配器。

select count(distinct(characters.name)) as matching_characters_count, shows.* from shows
  inner join characters on characters.show_id = shows.id and (characters.name = 'Batman' or characters.name = 'Robin')
group by shows.id
having matching_characters_count = 2

转换为ActiveRecord

Show.select('count(distinct(characters.name)) as matching_characters_count, shows.*').
   joins('inner join characters on characters.show_id = shows.id and (characters.name = "Batman" or characters.name = "Robin")').
   group('shows.id').
   having('matching_characters_count = 2')

然后你可能会通过插值进行传递,然后将它AREL起来;所以你不会构建字符串查询。

答案 2 :(得分:4)

我认为这必须奏效:

super_heroes = ['Batman', 'Robin']

Show.where(
  id: Character.select(:show_id).where(name: super_heroes).group(:show_id)
       .having("count(distinct characters.name) = #{super_heroes.size}")
)

我只是以Rails方式编写@sschmeck查询,作为子查询,并添加super_heroes var以显示如何缩放。

答案 3 :(得分:3)

使用聚合函数的纯SQL解决方案。 SQL语句返回您要查找的“所有”记录的ID值。

SELECT c.show_id
  FROM characters c
 WHERE c.name IN ('Batman','Robin')
 GROUP BY c.show_id
HAVING COUNT(DISTINCT c.name) = 2

您可以将此语句置于select_values()调用中,然后使用返回数组的值获取shows

答案 4 :(得分:3)

尽快减少查询中的条目数是获得更好的性能查询的基本思路。

Show.where("id IN (( SELECT show_id FROM characters WHERE name = 'Batman') INTERSECT (SELECT show_id FROM characters WHERE name = 'Robin'))")

在上面的查询中,第一个子查询获得了&#39; show_id&#39;对于蝙蝠侠&#39;,下一个获得&#39; show_id&#39;为了罗宾&#39;。在我看来,只有几个字符符合“INTERSECT&#39;”之后的条件。顺便说一句,你可以在&#39; rails db&#39;中使用explain命令。选择什么是更好的解决方案。

答案 5 :(得分:2)

我认为你正在寻找这个:

Show.includes(:characters).where(characters: {name: ["Batman", "Robin"]})

Show.includes(:characters).where('characters.name IN ?', ["Batman", "Robin"]).references(:characters)

references是必需的,因为我们在where方法中使用字符串

答案 6 :(得分:0)

首先,为什么你的方法是错的? 所以在&#34; characters_shows&#34;表,你可以找到这样的记录

show_id    name    character_id    character_name
 1     first show        1         batman
 2     second            1         batman
 1     first show        2         robin
 1     first show        3         superman

正如你所看到的那样,永远不会出现角色名称是同一行的蝙蝠侠和知更鸟的情况

另一种方法是这样的

names = ["Batman", "Robin"]

Show.joins(:characters).where(characters: {name: names}).group("shows.id").having("COUNT(*) = #{names.length}")