这是我正在尝试做的通用版本:
表recipes
包含字段id
和name
。表格ingredients
包含字段id
,name
和sweetness
,描述了该成分在1-10的范围内的甜度。食谱中含有许多成分和成分,因此两者在ingredients_recipes
表中相关,包含字段ingredient_id
和recipe_id
。
很容易找到含有甜度为10的成分的食谱。
SELECT DISTINCT recipes.* FROM recipes
INNER JOIN recipes_ingredients ri ON ri.recipe_id = recipes.id
INNER JOIN ingredients ON ingredients.id = ri.ingredient_id
WHERE ingredients.sweetness = 10
然而,我在否定该查询以找到含有 no 成分和甜味10的食谱时遇到了麻烦。我的第一个想法是:
SELECT DISTINCT recipes.* FROM recipes
INNER JOIN recipes_ingredients ri ON ri.recipe_id = recipes.id
INNER JOIN ingredients ON ingredients.id = ri.ingredient_id
WHERE ingredients.sweetness != 10
但是,它会找到包含任何非甜味-10 成分的食谱。
我的下一次尝试是以下,似乎有效:
SELECT * FROM recipes WHERE
(
SELECT count(*) FROM ingredients INNER JOIN recipes_ingredients ri ON
ri.ingredient_id = ingredients.id WHERE ingredients.sweetness = 10 AND
ri.recipe_id = recipes.id
) = 0
但是,我的一般经验是,与等效的,精心设计的JOIN相比,从属子查询的运行速度很慢。我一直在玩加入,分组等等,但是我不能完全围绕它,尤其是因为,虽然看起来像LEFT JOIN
和IS NULL
是合适的工具,但已经有两个联接讨厌。伟大的SQL向导,我可以运行什么查询以获得最佳结果?谢谢!
答案 0 :(得分:1)
试试这个:
SELECT DISTINCT recipes.*
FROM recipes r LEFT JOIN
(SELECT ri.recipe_id
FROM recipes_ingredients ri
INNER JOIN ingredients ON ingredients.id = ri.ingredient_id
WHERE ingredients.sweetness = 10) i on i.recipe_id=r.recipe_id
WHERE i.recipe_id is null
答案 1 :(得分:1)
尝试:
select
r.*
from
recipes r
where
not exists (
select
1
from
recipe_ingredients ri
join ingredients i on ri.ingredient_id = ri.ingredient_id
where
ri.recipie_id = r.recipe_id
and i.sweetness = 10
)
它仍然是一个相关的子查询,但exists
和not exists
进行了一些优化,使它们的性能优于原始查询。
对于直接联接解决方案,此应该工作:
select distinct
r.*
from
recipes r
join recipe_ingredients ri on ri.recipe_id = r.recipe_id
left join ingredents i on i.ingredient_id = ri.ingredient_id and i.sweetness = 10
where
i.ingredient_id is null
取决于索引,not exists
解决方案可能会更快,因为not exists
在确定是否有任何行满足给定条件时立即返回,而不会查看超出必要的表格。例如,如果它找到单行甜度10,它将停止查看该表并返回false。
答案 2 :(得分:0)
我在这里找到了给我的答案(我已经投票了),并且从他们的灵感中,我们提出了一个看起来似乎以惊人的出色表现完成工作的查询:
SELECT r.* FROM recipes r
LEFT JOIN recipes_ingredients ri ON ri.parent_id = r.id
LEFT JOIN ingredients i ON i.id = ri.ingredient_id AND i.sweetness = 10
GROUP BY r.id HAVING MAX(i.id) IS NULL
与内部条件的连接(受@Donnie启发)带出配方成分组合,如果成分不是甜味,则为NULL行。然后按配方ID分组,并选择“最大”成分ID。 (当且仅当没有要选择的实际ID时,MAX
函数将返回null,即,绝对没有与此配方相关联的非甜味-10项目可供选择。)如果该“最大”成分ID为null,然后MAX函数没有甜度-10项,因此选择行HAVING
为空MAX(i.id)
。
我在查询cacher已禁用的情况下多次运行查询的NOT EXISTS
版本和上述版本的查询。对于大约400个配方,NOT EXISTS
查询将始终需要大约1.0秒才能完成,而此查询的运行时通常大约为0.1秒。对于大约5000个食谱,NOT EXISTS
查询大约需要30秒,而上述查询通常需要0.1秒,并且几乎总是低于1.0。
值得注意的是,检查每个上的EXPLAINs,这里列出的查询几乎完全可以运行我给这些表的索引,这可能解释了为什么它能够进行各种加入和分组而不打击眼睛。另一方面,NOT EXISTS
查询必须执行从属子查询。如果这些索引没有到位,那么两者可能会更平等地执行,但是如果有机会使用原始联接,那么查询优化器相当强大,看起来就好了。
故事的道德:结构良好的JOIN非常强大:)非常感谢!