【问题标题】:Using group by and having clause使用 group by 和 having 子句
【发布时间】:2013-05-01 23:17:41
【问题描述】:

使用以下架构:

Supplier (sid, name, status, city)
Part (pid, name, color, weight, city)
Project (jid, name, city)
Supplies (sid, pid, jid**, quantity)
  1. 获取供应给至少两个不同项目的零件的供应商编号和名称。

  2. 为至少两个不同项目的同一部件的供应商获取供应商编号和名称。

这些是我的答案:

1.

SELECT s.sid, s.name
FROM Supplier s, Supplies su, Project pr
WHERE s.sid = su.sid AND su.jid = pr.jid
GROUP BY s.sid, s.name
HAVING COUNT (DISTINCT pr.jid) >= 2 

2.

SELECT s.sid, s.name
FROM Suppliers s, Supplies su, Project pr, Part p
WHERE s.sid = su.sid AND su.pid = p.pid AND su.jid = pr.jid
GROUP BY s.sid, s.name
HAVING COUNT (DISTINCT pr.jid)>=2

谁能确认我写的是否正确?我对 Group By 和 Have 子句的工作方式有点困惑

【问题讨论】:

    标签: sql


    【解决方案1】:

    拥有的语义

    为了更好地理解拥有,你需要从理论的角度来看待它。

    group by 是一个查询,它获取一个表并将其汇总到另一个表中。您可以通过将原始表分组为子集(基于您在分组依据中指定的属性)来汇总原始表。这些组中的每一个都将产生一个元组。

    Having 简单地等同于 WHERE 子句 after group by 已执行且在查询的 select 部分被计算之前。

    假设您的查询是:

    select a, b, count(*) 
    from Table 
    where c > 100 
    group by a, b 
    having count(*) > 10;
    

    这个查询的评估可以看成以下几个步骤:

    1. 执行 WHERE,消除不满足的行。
    2. 根据 a 和 b 的值将表分组为子集(每个子集中的每个元组都具有相同的 a 和 b 值)。
    3. 消除不满足 HAVING 条件的子集
    4. 处理每个输出值的子集,如查询的 SELECT 部分所示。这会为第 3 步之后留下的每个子集创建一个输出元组。

    您可以将其扩展到任何复杂的查询表可以是任何返回表的复杂查询(叉积、连接、UNION 等)。

    实际上,havesyntactic sugar,并没有扩展 SQL 的威力。任何给定的查询:

    SELECT list 
    FROM table
    GROUP BY attrList
    HAVING condition;
    

    可以改写为:

    SELECT list from (
       SELECT listatt 
       FROM table 
       GROUP BY attrList) as Name
    WHERE condition;
    

    listatt 是一个列表,包括 GROUP BY 属性以及列表和条件中使用的表达式。可能需要在此列表中命名一些表达式(使用 AS)。例如,上面的示例查询可以重写为:

    select a, b, count 
    from (select a, b, count(*) as count
          from Table 
          where c > 100
          group by a, b) as someName
    where count > 10;
    

    您需要的解决方案

    您的解决方案似乎是正确的:

    SELECT s.sid, s.name
    FROM Supplier s, Supplies su, Project pr
    WHERE s.sid = su.sid AND su.jid = pr.jid
    GROUP BY s.sid, s.name
    HAVING COUNT (DISTINCT pr.jid) >= 2 
    

    你加入三个表,然后使用sid作为分组属性(sname在功能上依赖它,所以对组数没有影响,但是你必须包含它,否则它不能是语句选择部分的一部分)。然后您将删除那些不满足您的条件的:满足pr.jid is >= 2,这是您最初想要的。

    问题的最佳解决方案

    我个人更喜欢更简单的清洁解决方案:

    1. 您只需按耗材(sid、pid、jid**、数量)分组即可 找到至少为两个项目提供服务的 sid。
    2. 然后将其加入到 Suppliers 表中以获得相同的供应商。

     SELECT sid, sname from
        (SELECT sid from supplies 
        GROUP BY sid 
        HAVING count(DISTINCT jid) >= 2
        ) AS T1
    NATURAL JOIN 
    Supliers;
    

    执行起来也会更快,因为连接只在需要时完成,而不是一直。

    --dmg

    【讨论】:

    • SELECT sid from quantity?你在哪里看到那张桌子的?
    • 可以SELECT count(sid), sname FROM Supplier GROUP BY name HAVING name = 'Alice'吗?
    • 是的。 name 是 group by 结果的属性。但最好在 where 子句中(在 group by 之前)进行。
    • 是不是“FROM Supplier s, Supplies su, Project pr”在三个表之间做交叉连接或者笛卡尔积?如果这是肯定的,那么如果这些表中存在大量数据,那么这可能会损害性能真的很糟糕。
    • @JoséAlvarez 是的,连接可能是一项昂贵的操作。在原始查询中,名称是为每个元组连接的,而它仅用于结果。因此,改进后的查询不仅更易于阅读 (IMO),而且很可能执行得更快。
    【解决方案2】:

    因为我们不能将 Where 子句与 聚合函数 count()、min()、sum() 等一起使用,所以有子句的存在就是为了克服这个问题sql中的问题。请参阅让子句通过此链接的示例

    http://www.sqlfundamental.com/having-clause.php

    【讨论】:

    • 有另一个链接?那个坏了:(
    【解决方案3】:

    首先,您应该使用JOIN 语法而不是FROM table1, table2,并且您应该始终将分组限制为您需要的尽可能少的字段。

    虽然我没有测试过,你的第一个查询对我来说似乎很好,但可以重写为:

    SELECT s.sid, s.name
    FROM 
        Supplier s
        INNER JOIN (
           SELECT su.sid
           FROM Supplies su
           GROUP BY su.sid
           HAVING COUNT(DISTINCT su.jid) > 1
        ) g
            ON g.sid = s.sid
    

    或简化为:

    SELECT sid, name
    FROM Supplier s
    WHERE (
        SELECT COUNT(DISTINCT su.jid)
        FROM Supplies su
        WHERE su.sid = s.sid
    ) > 1
    

    但是,您的第二个查询对我来说似乎是错误的,因为您也应该GROUP BY pid

     SELECT s.sid, s.name
        FROM 
            Supplier s
            INNER JOIN (
                SELECT su.sid
                FROM Supplies su
                GROUP BY su.sid, su.pid
                HAVING COUNT(DISTINCT su.jid) > 1
            ) g
                ON g.sid = s.sid
    

    您可能已经在上面的查询中注意到,我使用INNER JOIN 语法来执行过滤,但它也可以写成:

    SELECT s.sid, s.name
    FROM Supplier s
    WHERE (
         SELECT COUNT(DISTINCT su.jid)
         FROM Supplies su
         WHERE su.sid = s.sid
         GROUP BY su.sid, su.pid
    ) > 1
    

    【讨论】:

    • 谢谢!我们还没有学习内部连接,所以我不知道如何使用它。不过,我会将 GROUP BY pid 语句添加到第二个查询中。
    • 听起来你的讲师有点过时了。你已经在使用inner joins,你只是在使用旧的语法。
    【解决方案4】:

    正在使用什么类型的 sql 数据库(MSSQL、Oracle 等)? 我相信你写的是正确的。

    你也可以这样写第一个查询:

    SELECT s.sid, s.name
    FROM Supplier s
    WHERE (SELECT COUNT(DISTINCT pr.jid)
           FROM Supplies su, Projects pr
           WHERE su.sid = s.sid 
               AND pr.jid = su.jid) >= 2
    

    与尝试使用 GROUP BY 相比,它更具可读性,而且更不费神。但性能可能会有所不同。

    【讨论】:

    • 是的,使用 Ms SQL。感谢您的回复。对于分配,我们假设使用 GROUP BY 子句,所以我不确定重写它是否会对我有所帮助。我同意 GROUP BY 很难理解!
    【解决方案5】:

    1.获取供应给至少两个不同项目的零件供应商的供应商编号和名称。

     SELECT S.SID, S.NAME
     FROM SUPPLIES SP
     JOIN SUPPLIER S
     ON SP.SID = S.SID
     WHERE PID IN
     (SELECT PID FROM SUPPPLIES GROUP BY PID, JID HAVING COUNT(*) >= 2)
    

    你的第二个问题我不是很清楚

    【讨论】:

      猜你喜欢
      • 2021-08-15
      • 2016-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多