我有两个表,Show
,Character
。每个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,即使有明确的节目与蝙蝠侠和罗宾相关联。
答案 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}")