如何在联接表中查找具有所有必需的has_many关联的记录,而不是OR / IN查询

时间:2019-04-18 14:51:09

标签: ruby activerecord sinatra

我有一个餐厅表,一个美食表和一个连接多对多的联接表。我想找到一个匹配两种特定美食的餐厅,即找到联接表中列出的与“美食1”和“美食2”相关的餐厅。

我可以将它们和包含在一起的东西一起破解,但是感觉好像在使用ActiveRecord构建查询时肯定缺少一些直接而明显的东西。

Restaurant
ID| Name
1 | McDonalds
2 | Taco Bell

Cuisine
ID| Style
1 | American
2 | Mexican 
3 | Fast Food 

Restaurant_Cuisines
ID | Restaurant_ID | Cuisine_ID
1  | 1             | 1
2  | 1             | 3
3  | 2             | 2
4  | 2             | 3

我希望能够查询同时提供American&Fast Food的餐厅,这会让我回想起McDonalds,但没有Taco Bell,因为Taco Bell提供Fast快餐,但没有美国人。

2 个答案:

答案 0 :(得分:2)

我可以想到以下查询,它可能不是最优化的解决方案,但它给出了正确的答案,并且可以指导获得最佳答案的方向。

rest_ids = Restaurant_Cuisines.where(Cuisine_ID: 1).pluck(:Restaurant_ID) && Restaurant_Cuisines.where(Cuisine_ID: 3).pluck(:Restaurant_ID)
Restaurant.where(id: rest_ids)

如果需要概括:

def specific_restaurant(cuisine_ids)
  ids = cuisine_ids.map { |id| Restaurant_ID.where(Cuisine_ID: id).pluck(:Restaurant_ID) }.reduce(:&)
  Restaurant.where(id: ids) if ids.present?
end

绝对是N+1,其中Ncuisine_ids,但是如果N受限制/很少,则不会造成伤害。

更新-最后,一次查询!

def specific_restaurant(cuisine_ids)
  ids = RestaurantCuisine.where(cuisine_id: cuisine_ids).group(:restaurant_id).having("count(*) = #{cuisine_ids.count}").pluck(:restaurant_id)
  Restaurant.where(id: ids) if ids.present?
end

答案 1 :(得分:0)

假设您具有外键的ID,则可以使用joins并像这样传递联接表的ID:

cuisine_ids = Cuisine.where(Style: ['American', 'Mexican']).pluck(:id)

restaurants = Restaurant.joins(:cuisines).where(cuisines: {id: cuisine_ids})