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 子句的工作方式有点困惑

4

5 回答 5

101

拥有的语义

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

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

Have等同于在group by 执行之后和查询的 select 部分计算之前的WHERE子句

假设您的查询是:

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 等)。

事实上,have语法糖,并没有扩展 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在功能上是依赖它的,所以对组数没有影响,但是一定要包含,否则不能成为select部分的一部分该声明)。然后,您将删除那些不满足您的条件的:满足pr.jid is >= 2,这是您最初想要的。

您问题的最佳解决方案

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

  1. 您只需按供应品(sid、pid、jid**、数量)进行分组,即可找到至少为两个项目供应的供应品的 sid。
  2. 然后将其加入供应商表以获取相同的供应商。

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

执行起来也会更快,因为仅在需要时才进行连接,而不是一直进行。

--dmg

于 2013-05-02T05:26:22.913 回答
6

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

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

于 2014-01-29T07:12:45.397 回答
1

首先,您应该使用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
于 2013-05-01T23:51:52.200 回答
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)

我不知道你的第二个问题

于 2013-05-02T05:48:36.167 回答
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 相比,它更具可读性,并且不那么令人费解。性能可能会有所不同。

于 2013-05-01T23:43:35.033 回答