如何在SQL的WHERE中和其上链接多态?

时间:2019-02-18 15:44:03

标签: sql ruby-on-rails postgresql

我想做类似的事情:

SELECT  "recipes"."id"
FROM "recipes"
    INNER JOIN "groups" ON "groups"."recipe_id" = "recipes"."id"
    INNER JOIN "steps" ON "steps"."group_id" = "groups"."id"
    INNER JOIN "steps_ingredients_memberships" ON "steps_ingredients_memberships"."step_id" = "steps"."id"
    INNER JOIN "ingredients" ON "ingredients"."id" =  "steps_ingredients_memberships"."ingredient_id"
 WHERE (ingredients.id IN (5, 6) AND ingredients.id IN (10, 11))
 LIMIT 10

但是此请求返回0行...
我知道此请求无法正常工作,但找不到解决方法

我想获取包含成分5 OR 6 AND 10 OR 11。 像这样思考:

5 = tomatoes
6 = big tomatoes
10 = potatoes
11 = big potatoes

我想要西红柿OR大西红柿AND土豆OR大土豆的“食谱”。

采用的解决方案

因为成分可以是随机的,所以我写了一个范围:

  scope :ingredients_filtering, lambda { |ingredients|
    return if ingredients.nil?

    queries = []
    ingredients.each do |ingredient|
      related_ids = ingredient.related_ids.uniq.join(', ')
      queries << "BOOL_OR(ingredients.id IN (#{related_ids}))"
    end
    group(:id).having(queries.join(' AND '))
  }

谢谢大家! :)

3 个答案:

答案 0 :(得分:2)

您要聚合。按配方分组,看看它是否具有所需的成分。

$("#inputBox").on("input", throttle(2000, function(evt) {
  myFunctionToThrottle(evt);
}));

答案 1 :(得分:1)

要在@MrYoshiji's评论Here上进行扩展,我们可以使它更加“肮脏”,如下所示:

ingredients_table = Ingredient.arel_table
ingredient_list1 = [5,6]
ingredient_list2 = [10,11]
condition = Arel::Nodes::NamedFunction.new(
              'BOOL_OR',[ingredients_table[:id].in(ingredient_list1)]
            ).and(
              Arel::Nodes::NamedFunction.new(
                'BOOL_OR',[ingredients_table[:id].in(ingredient_list2)]
              )
            )
recipes = Recipe.joins(groups: { 
                       steps: { 
                         steps_ingredients_memberships: :ingredients
                       }
                     })
        .group('recipes.id')
        .having(condition)

这将产生类似于链接答案的SQL :(由@ThorstenKettner提供)例如

SELECT recipes.*
FROM recipes 
  JOIN groups ON groups.recipe_id = recipes.id 
  JOIN steps ON steps.group_id = groups.id 
  JOIN steps_ingredients_memberships ON steps_ingredients_memberships.step_id = steps.id 
  JOIN ingredients ON ingredients.id = steps_ingredients_memberships.ingredient_id 
GROUP BY 
   recipes.id
HAVING 
  BOOL_OR(ingredients.id IN (5, 6)) AND 
  BOOL_OR(ingredients.id IN (10, 11))

但是,它提供了以更有效的方式更改ingredient_list1ingredient_list2的灵活性,而无需担心转义等问题。

顺便说一句以发表您的评论:

  

“我放置了ruby-on-rails标签,因为如果在ruby-on-rails中有解决方案,那就更好了,但我认为不是...”

arel可以汇编您可以想象的任何查询(它也可以汇编无效/分段的SQL)。如果它是有效的,则SQL ActiveRecord可以运行它,因此,如果您使用的是rails且查询问题肯定像您一样包含标记。

更新(根据您发布的解决方案),但使用arel而不是字符串串联

scope :ingredients_filtering, lambda { |ingredients|
  return unless ingredients
  ingredients_table = Ingredient.arel_table
  conditions = ingredients.map do |ingredient|
    Arel::Nodes::NamedFunction.new(
          'BOOL_OR',[ingredients_table[:id].in(ingredient.related_ids)]
        )
  end.reduce(&:and)
  group(:id).having(conditions)
}

答案 2 :(得分:0)

使用GROUP BYHAVING

SELECT  "recipes"."id"
FROM "recipes"
    INNER JOIN "groups" ON "groups"."recipe_id" = "recipes"."id"
    INNER JOIN "steps" ON "steps"."group_id" = "groups"."id"
    INNER JOIN "steps_ingredients_memberships" ON "steps_ingredients_memberships"."step_id" = "steps"."id"
    INNER JOIN "ingredients" ON "ingredients"."id" =  "steps_ingredients_memberships"."ingredient_id"
 GROUP BY "recipes"."id"
 HAVING COUNT(CASE WHEN ingredients.id IN (5, 6) THEN 1 END) > 0 
    AND COUNT(CASE WHEN ingredients.id IN (10, 11) THEN 1 END) > 0