拥有的语义
为了更好地理解拥有,你需要从理论的角度来看待它。
group by 是一个查询,它获取一个表并将其汇总到另一个表中。您可以通过将原始表分组为子集(基于您在分组依据中指定的属性)来汇总原始表。这些组中的每一个都将产生一个元组。
Having 简单地等同于 WHERE 子句 after group by 已执行且在查询的 select 部分被计算之前。
假设您的查询是:
select a, b, count(*)
from Table
where c > 100
group by a, b
having count(*) > 10;
这个查询的评估可以看成以下几个步骤:
- 执行 WHERE,消除不满足的行。
- 根据 a 和 b 的值将表分组为子集(每个子集中的每个元组都具有相同的 a 和 b 值)。
- 消除不满足 HAVING 条件的子集
- 处理每个输出值的子集,如查询的 SELECT 部分所示。这会为第 3 步之后留下的每个子集创建一个输出元组。
您可以将其扩展到任何复杂的查询表可以是任何返回表的复杂查询(叉积、连接、UNION 等)。
实际上,have 是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 属性以及列表和条件中使用的表达式。可能需要在此列表中命名一些表达式(使用 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,这是您最初想要的。
问题的最佳解决方案
我个人更喜欢更简单的清洁解决方案:
- 您只需按耗材(sid、pid、jid**、数量)分组即可
找到至少为两个项目提供服务的 sid。
- 然后将其加入到 Suppliers 表中以获得相同的供应商。
SELECT sid, sname from
(SELECT sid from supplies
GROUP BY sid
HAVING count(DISTINCT jid) >= 2
) AS T1
NATURAL JOIN
Supliers;
执行起来也会更快,因为连接只在需要时完成,而不是一直。
--dmg