使用group by和having子句

时间:2013-05-01 23:17:42

标签: sql

使用以下架构:

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

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

  3. 这些是我的答案:

    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和Having子句如何工作,我有点困惑

6 个答案:

答案 0 :(得分:92)

拥有

的语义

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

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

拥有只是等同于分组后的WHERE子句已执行并且在计算查询的 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之后为每个子集创建一个输出元组。
  5. 您可以将此扩展到任何复杂查询。表可以是任何返回表的复杂查询(交叉产品,连接,UNION等)。

    事实上, syntactic 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属性以及list和condition中使用的表达式。可能需要在此列表中命名一些表达式(使用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. 然后将其加入供应商表,以使供应商获得相同的信息。
    3.  SELECT sid, sname from
          (SELECT sid from supplies 
          GROUP BY sid, pid 
          HAVING count(DISTINCT jid) >= 2
          ) AS T1
      NATURAL JOIN 
      Supliers;
      

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

      - DMG

答案 1 :(得分:5)

因为我们不能使用聚合函数的Where子句,如 count(),min(),sum()等所以有条款来克服这个sql中的问题。请参阅有关子句的示例通过此链接

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

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

使用什么类型的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进行操作相比,它更具可读性,并且不那么令人费解。但性能可能会有所不同。

答案 4 :(得分:0)

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)

我对你的第二个问题并不感兴趣

答案 5 :(得分:0)

Having:它对每组行应用过滤条件。 其中:它应用单个行的过滤器。