【问题标题】:Sub-queries filtering too many rows过滤太多行的子查询
【发布时间】:2020-02-23 19:15:43
【问题描述】:

我的子查询过滤掉了太多结果。它应该在子查询中过滤掉 3 个配方中使用的 12 种成分。共有 79 种成分,因此查询应返回 67 行。目前我的查询返回 54。

我不确定为什么,但是如果我将第二个 WHERE 条件更改为 OR 而不是 AND,我会得到 68 行,这与我的预期相差无几

我正在尝试创建的查询:

(5) 找出爱尔兰炖菜中未使用的所有成分的 ID 和名称, Pollo Picoso,或烤牛肉。 (2 列,67 行)

select distinct Recipes.RecipeID, Ingredients.IngredientName
from Recipes
inner join Recipe_Ingredients on Recipes.RecipeID = Recipe_Ingredients.RecipeID
inner join Ingredients on Recipe_Ingredients.IngredientID = Ingredients.IngredientID
where Ingredients.IngredientID NOT IN
    (select distinct Ingredients.IngredientID
    from Recipes
    join Recipe_Ingredients on Recipes.RecipeID = Recipe_Ingredients.RecipeID
    join Ingredients on Recipe_Ingredients.IngredientID = Ingredients.IngredientID
    where Recipes.RecipeTitle = 'Roast Beef')
and Ingredients.IngredientID NOT IN
    (select distinct Ingredients.IngredientID
    from Recipes
    join Recipe_Ingredients on Recipes.RecipeID = Recipe_Ingredients.RecipeID
    join Ingredients on Recipe_Ingredients.IngredientID = Ingredients.IngredientID
    where Recipes.RecipeTitle = 'Irish Stew')
and Ingredients.IngredientID NOT IN
    (select distinct Ingredients.IngredientID
    from Recipes
    join Recipe_Ingredients on Recipes.RecipeID = Recipe_Ingredients.RecipeID
    join Ingredients on Recipe_Ingredients.IngredientID = Ingredients.IngredientID
    where Recipes.RecipeTitle = 'Pollo Picoso');

数据库图:

DB Fiddble I created

【问题讨论】:

  • 首次使用not exists
  • 查找所有成分的 ID 和名称如果您只需要成分,为什么要选择 Recipes.RecipeID

标签: sql sql-server tsql join left-join


【解决方案1】:

我更喜欢只在对加入的结果感兴趣时加入。这不是这里的情况。您只想查看符合某些条件的成分。那是FROM ingredients WHERE ...。条件属于WHERE 子句。因此,您会得到一个易于理解并因此易于维护的查询。

另一方面,DISTINCT 通常是一个写得很糟糕的查询的标志(只是加入所有内容,然后抓住一些方法来摆脱我们自己不必要地产生的东西)。这也可能非常昂贵,因为这会产生更大的中间结果,然后必须对其进行排序以找到创建的重复项。

我会使用 NOT ININ 来获取所有不在配方中“烤牛肉”的配料组中的配料、“爱尔兰炖肉”和“波罗·皮科索”。

select ingredientid, ingredientname
from ingredients 
where ingredientid not in 
(
  select ingredientid
  from recipe_ingredients
  where recipeid in
  (
    select recipeid
    from recipes 
    where recipetitle in ('Roast Beef', 'Irish Stew', 'Pollo Picoso')
  )
)
order by ingredientid;

【讨论】:

    【解决方案2】:

    这个查询:

    select distinct ri.IngredientID
    from Recipe_Ingredients ri inner join Recipes r
    on r.RecipeID = ri.RecipeID
    where r.RecipeTitle in ('Roast Beef', 'Irish Stew', 'Pollo Picoso')
    

    返回要排除的 12 种成分。
    只有表 Recipe_IngredientsRecipes 需要连接。
    现在将 Ingredients 加入该查询并仅返回不匹配的行:

    select i.IngredientID, i.IngredientName
    from Ingredients i
    left join (
      select ri.IngredientID
      from Recipe_Ingredients ri inner join Recipes r
      on r.RecipeID = ri.RecipeID
      where r.RecipeTitle in ('Roast Beef', 'Irish Stew', 'Pollo Picoso')
    ) t on t.IngredientID = i.IngredientID  
    where t.IngredientID is null
    

    您也可以使用NOT IN 获得相同的结果:

    select IngredientID, IngredientName
    from Ingredients 
    where IngredientID not in (
      select ri.IngredientID
      from Recipe_Ingredients ri inner join Recipes r
      on r.RecipeID = ri.RecipeID
      where r.RecipeTitle in ('Roast Beef', 'Irish Stew', 'Pollo Picoso')
    )
    

    请参阅demo
    结果是 67 行配料。

    【讨论】:

    • @Tomarik 真的吗?你会使用 3 个嵌套的 IN 吗?
    【解决方案3】:

    我猜你只需要 -

    select distinct R.RecipeID, I.IngredientName
    from Recipes R
    inner join Recipe_Ingredients RI on R.RecipeID = RI.RecipeID
    inner join Ingredients I on RI.IngredientID = I.IngredientID
    where R.RecipeTitle not in ('Roast Beef',  'Irish Stew',  'Pollo Picoso');
    

    如果这不符合您的要求,请告诉我。

    【讨论】:

      猜你喜欢
      • 2016-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-08
      • 2017-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多