【问题标题】:Counting Distinct Records Using Multiple Criteria From Another Table In MySQL使用 MySQL 中另一个表中的多个条件计算不同的记录
【发布时间】:2011-05-12 10:41:39
【问题描述】:

这不是家庭作业。我已更改表和字段的名称,仅用于说明目的。我承认我对 MySQL 完全陌生。请在您的回答中考虑到这一点。

说明我需要的查询功能的最好方法是这样的:

我有两张桌子。

一个表与另一个表的关系为 0..1 到 0..n。

为了简单起见,假设这两个表是配方和成分。

成分表中的一个字段引用配方表,但可能为空。

例如:

我想知道类似以下内容的 SQL:有多少食谱需要“1”数量的“橄榄”和“2”数量的“蘑菇”

作为结构化查询语言的新手,我什至不知道该用谷歌搜索什么信息。

我在以下方面是否走在正确的轨道上?:

SELECT COUNT(DISTINCT Recipe.ID) AS Answer FROM Recipe, Ingredient 
  WHERE Ingredient.RecipeID=Recipe.ID AND Ingredient.Name='Olives'
  AND Ingredient.Amount=1 AND Ingredient.Name='Mushrooms'
  AND Ingredient.Amount=2

我意识到这是完全错误的,因为名称不能同时是橄榄和蘑菇......但不知道该放什么,因为我需要计算两者的所有食谱,但只计算两者的所有食谱。

如何正确地为 MySQL 编写这样的查询?

【问题讨论】:

    标签: sql mysql relational-database


    【解决方案1】:

    你很亲密。

    试试类似的东西

    SELECT  COUNT(Recipe.ID) Answer
    FROM    Recipe INNER JOIN
            Ingredient olives ON olives.RecipeID=Recipe.ID INNER JOIN
            Ingredient mushrooms ON mushrooms.RecipeID=Recipe.ID 
    WHERE   olives.Name='Olives'
    AND     mushrooms.Name='Mushrooms'
    AND     olives.Amount = 1
    AND     mushrooms.Amount = 2
    

    您可以加入同一张表两次,您只需为该表指定一个适当的alias

    【讨论】:

    • 别忘了 SQL 中的字符串使用单引号。
    • 行不通。无需进行 2 加入,您使用的是相同的条件。您只是将相同的数据连接了两次。
    • @Vincent Savard,我想你可能理解错了这个问题?
    • 起初很有可能,但我看不出这将如何选择好的数据。假设您有 {'Ingredient 1', 1} 其中 1 是对 {1, 'Olive'} 的引用,即使您进行了两次显式连接,元组最终也会成为 {'Ingredient 1', 1, 1,“橄榄”,1,“橄榄”}。 RecipeID 不会改变。
    【解决方案2】:

    用途:

    SELECT COUNT(r.id)
      FROM RECIPE r
     WHERE EXISTS(SELECT NULL
                    FROM INGREDIENTS i
                   WHERE i.recipeid = r.id
                     AND i.name = 'Olives'
                     AND i.amount = 1)
       AND EXISTS(SELECT NULL
                    FROM INGREDIENTS i
                   WHERE i.recipeid = r.id
                     AND i.name = 'Mushrooms'
                     AND i.amount = 2)
    

    【讨论】:

    • 真正的问题,是不是比简单的join更有效率?
    • @Vincent Savard:JOIN 并不总是最好的方法——一对多的关系会增加返回的记录数量,需要 distinct 或 GROUP BY 子句。根据所需的结果,GROUP BY 可能会使事情复杂化。
    • 似乎有很多不同的方法可以做到这一点。由于我刚刚开始,我不想养成任何坏习惯。我应该如何选择最佳方法?您的答案与 ajreal 相比有何优缺点?
    • @Joshua:我建议旁观者回答 - 如果不需要,请不要使用 OR。
    • @Roopesh Shenoy:实际上,SELECT 子句永远不会在 EXISTS 子句中进行评估 - 试试EXISTS(SELECT 1/0)。它应该返回除以零的错误,但它不会。
    【解决方案3】:
    SELECT COUNT(DISTINCT Recipe.ID) AS Answer 
    FROM Recipe, Ingredient as ing1, Ingredient as ing2
    WHERE 
      Ing1.RecipeID=Recipe.ID AND Ing1.Name="Olives" AND ing1.Amount=1 AND 
      Ing2.RecipeID=Recipe.ID AND Ing2.Name="Mushrooms" AND ing2.Amount=2;
    

    希望有帮助

    【讨论】:

    • 别忘了 SQL 中的字符串使用单引号。
    • 这行不通。即使您连接两个表,在同一行中也永远不会有一列等于 Olives 和一列等于 Mushrooms。无论如何,你不需要两个连接来做 OP 想要的。
    • 在所有答案中,这个对我来说最有意义。你知道它在效率上与 :JOIN" 相比如何吗?
    • @Joshua :正如我在回答中所说,两种语法都创建相同的查询计划,不会对性能产生任何影响。只是……
    • @Joshua:JOIN 是 ANSI-92 语法;这是 ANSI-89 JOIN 语法。
    【解决方案4】:

    首先,您使用旧的连接语法。 INNER JOIN 创建相同的执行计划,但更加清晰。然后,您的查询完全是关于一个简单的旧条件。你只需要正确地写它们! (这需要一些练习,我同意。)

    SELECT COUNT(DISTINCT R.ID) Answer
    FROM Recipe R
    INNER JOIN Ingredient I
        ON R.ID = I.RecipeID
        AND (R.Name = 'Olives' AND I.Amount = 1)
        OR (R.Name = 'Mushrooms' AND I.Amount = 2);
    

    这是我的示例数据:

    mysql> SELECT * FROM Ingredient;
    +------+------+--------+----------+
    | ID   | Name | Amount | RecipeID |
    +------+------+--------+----------+
    |    1 | I1   |      1 |        1 |
    |    1 | I2   |      2 |        1 |
    |    1 | I3   |      2 |        1 |
    |    1 | I4   |      3 |        1 |
    |    1 | I1   |      1 |        2 |
    |    1 | I2   |      1 |        2 |
    |    1 | I3   |      3 |        2 |
    |    1 | I4   |      2 |        2 |
    |    1 | I1   |      2 |        3 |
    |    1 | I2   |      1 |        3 |
    +------+------+--------+----------+
    10 rows in set (0.00 sec)
    
    mysql> SELECT * FROM Recipe;
    +------+-----------+
    | ID   | Name      |
    +------+-----------+
    |    1 | Mushrooms |
    |    2 | Olives    |
    |    3 | Tomatoes  |
    +------+-----------+
    3 rows in set (0.00 sec)
    

    我的查询应该输出 2。

    编辑:实际上,我意识到我的查询选择了错误的行。这可以正常工作:

    SELECT COUNT(DISTINCT R.Id) Answer 
    FROM Recipe R 
    INNER JOIN Ingredient I 
        ON R.ID = I.RecipeID 
    WHERE 
        (R.Name = 'Mushrooms' AND I.Amount = 2) 
        OR (R.Name = 'Olives' AND I.Amount = 1);
    

    也输出 2。

    【讨论】:

    • 为什么在查询的最后一行说“OR”?
    • 因为它要么是橄榄 + 数量 1 要么是蘑菇 + 数量 2。您可以将 COUNT 替换为 *,这样您就可以看到实际选择的内容。
    • 但我想要包含橄榄和蘑菇的食谱。对不起。我想我只是不够先进,无法理解这种方法。
    • @Joshua:啊,我的错,在这种情况下,OMG Ponies 的解决方案很可能是最好的。我以为你想要其中一个的食谱数量,而不是两者。
    【解决方案5】:

    仅供参考……

    有多少食谱需要“1”数量的“橄榄”和“2”数量的“蘑菇”

    查看上面的表格结构,您可能希望 associative entity 在食谱和配料之间切换。这使得使用 inner join 进行所需的查询变得容易得多。

    在你的两张桌子之间,我会想象这样的事情......

    Recipe_Ingredient
    -----------------
    (PK, FK) RecipeID
    (PK, FK) IngredientID
    

    如果你有这个,那么每个食谱可能有很多成分,并且每种成分可能是许多食谱的一部分。一旦你有一个这样的表来正确关联你的两个表,你可以将它们连接在一起并获得一个完整的食谱。对于像这样的更复杂的查询,您有两个食谱的单独条件,我可能会对其进行子查询以帮助理解。

    SELECT SUM(RecipeCount) as RecipeCount
    FROM
    (
        SELECT COUNT(r.*) as RecipeCount
        FROM Recipe r
        INNER JOIN Recipe_Ingredient ri on r.ID = ri.RecipeID
        INNER JOIN Ingredient i on i.ID = ri.IngredientID
        WHERE i.Name = 'Olives' AND i.Amount = 1
    UNION ALL
        SELECT COUNT(r.*) as RecipeCount
        FROM Recipe r
        INNER JOIN Recipe_Ingredient ri on r.ID = ri.RecipeID
        INNER JOIN Ingredient i on i.ID = ri.IngredientID
        WHERE i.Name = 'Mushrooms' AND i.Amount = 2
    ) as subTable
    

    【讨论】:

    • 虽然我完全清楚 - 并且符合我示例的上下文,但它确实只是一个示例,这种多对多关系不适合我涉及英语语法的实际任务解析。
    • 可以理解!我只是想把它扔出去。还有很多其他好的答案可供选择,但没有人提出这个。
    • 当您只需要一个成分的类型代码时,您添加了一个多对多表......然后您使用 UNION 而将返回一个唯一的计数值列表 - 如果两者都是五个,你只会得到一个值 5。其中,没有什么可以确保那些有橄榄的列表中包括蘑菇,反之亦然。这不是可以考虑使用的答案。
    • 你对工会的看法是对的,我的错。我认为工会都应该纠正这一点。不过,我不确定我是否理解您对类型代码的了解。您能否阐明如何在不重复成分数据的情况下为食谱提供多种成分?
    猜你喜欢
    • 2011-09-26
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 2019-11-19
    • 1970-01-01
    • 2014-09-20
    • 1970-01-01
    相关资源
    最近更新 更多